mirror of
https://github.com/mediacms-io/mediacms.git
synced 2026-02-04 06:22:59 -05:00
all
This commit is contained in:
206
lms-plugins/mediacms-moodle/tiny/mediacms/AUTOCONVERT.md
Executable file
206
lms-plugins/mediacms-moodle/tiny/mediacms/AUTOCONVERT.md
Executable file
@@ -0,0 +1,206 @@
|
||||
# MediaCMS URL Auto-Convert Feature
|
||||
|
||||
This feature automatically converts pasted MediaCMS video URLs into embedded video players within the TinyMCE editor.
|
||||
|
||||
## Overview
|
||||
|
||||
When a user pastes a MediaCMS video URL like:
|
||||
```
|
||||
https://deic.mediacms.io/view?m=JpBd1Zvdl
|
||||
```
|
||||
|
||||
It is automatically converted to an embedded video player:
|
||||
```html
|
||||
<div class="tiny-iframe-responsive" contenteditable="false">
|
||||
<iframe
|
||||
style="width: 100%; max-width: calc(100vh * 16 / 9); aspect-ratio: 16 / 9; display: block; margin: auto; border: 0;"
|
||||
src="https://deic.mediacms.io/embed?m=JpBd1Zvdl&showTitle=1&showRelated=1&showUserAvatar=1&linkTitle=1"
|
||||
allowfullscreen="allowfullscreen">
|
||||
</iframe>
|
||||
</div>
|
||||
```
|
||||
|
||||
## Supported URL Formats
|
||||
|
||||
The auto-convert feature recognizes MediaCMS view URLs in this format:
|
||||
- `https://[domain]/view?m=[VIDEO_ID]`
|
||||
|
||||
Examples:
|
||||
- `https://deic.mediacms.io/view?m=JpBd1Zvdl`
|
||||
- `https://your-mediacms-instance.com/view?m=abc123`
|
||||
|
||||
## Configuration
|
||||
|
||||
### Accessing Settings
|
||||
|
||||
1. Log in to Moodle as an administrator
|
||||
2. Navigate to: **Site administration** → **Plugins** → **Text editors** → **TinyMCE editor** → **MediaCMS**
|
||||
3. Scroll to the **Auto-convert MediaCMS URLs** section
|
||||
|
||||
### Available Settings
|
||||
|
||||
| Setting | Description | Default |
|
||||
|---------|-------------|---------|
|
||||
| **Enable auto-convert** | Turn the auto-convert feature on or off | Enabled |
|
||||
| **MediaCMS base URL** | Restrict auto-conversion to a specific MediaCMS domain | Empty (allow all) |
|
||||
| **Show video title** | Display the video title in the embedded player | Enabled |
|
||||
| **Link video title** | Make the video title clickable, linking to the original video page | Enabled |
|
||||
| **Show related videos** | Display related videos after the current video ends | Enabled |
|
||||
| **Show user avatar** | Display the uploader's avatar in the embedded player | Enabled |
|
||||
|
||||
### Settings Location in Moodle
|
||||
|
||||
The settings are stored in the Moodle database under the `tiny_mediacms` plugin configuration:
|
||||
|
||||
- `tiny_mediacms/autoconvertenabled` - Enable/disable auto-convert
|
||||
- `tiny_mediacms/autoconvert_baseurl` - MediaCMS base URL (e.g., https://deic.mediacms.io)
|
||||
- `tiny_mediacms/autoconvert_showtitle` - Show title option
|
||||
- `tiny_mediacms/autoconvert_linktitle` - Link title option
|
||||
- `tiny_mediacms/autoconvert_showrelated` - Show related option
|
||||
- `tiny_mediacms/autoconvert_showuseravatar` - Show user avatar option
|
||||
|
||||
### Base URL Configuration
|
||||
|
||||
The **MediaCMS base URL** setting controls which MediaCMS instances are recognized for auto-conversion:
|
||||
|
||||
- **Empty (default)**: Any MediaCMS URL will be auto-converted (e.g., URLs from any `*/view?m=*` pattern)
|
||||
- **Specific URL**: Only URLs from the specified domain will be auto-converted
|
||||
|
||||
Example configurations:
|
||||
- `https://deic.mediacms.io` - Only convert URLs from deic.mediacms.io
|
||||
- `https://media.myuniversity.edu` - Only convert URLs from your institution's MediaCMS
|
||||
|
||||
## Technical Details
|
||||
|
||||
### File Structure
|
||||
|
||||
```
|
||||
amd/src/
|
||||
├── autoconvert.js # Main auto-convert module
|
||||
├── plugin.js # Plugin initialization (imports autoconvert)
|
||||
└── options.js # Configuration options definition
|
||||
|
||||
classes/
|
||||
└── plugininfo.php # Passes PHP settings to JavaScript
|
||||
|
||||
settings.php # Admin settings page definition
|
||||
|
||||
lang/en/
|
||||
└── tiny_mediacms.php # Language strings for settings
|
||||
```
|
||||
|
||||
### How It Works
|
||||
|
||||
1. **Paste Detection**: The `autoconvert.js` module listens for `paste` events on the TinyMCE editor
|
||||
2. **URL Validation**: When text is pasted, it checks if it matches the MediaCMS URL pattern
|
||||
3. **HTML Generation**: If valid, it generates the responsive iframe HTML with configured options
|
||||
4. **Content Insertion**: The original URL is replaced with the embedded video
|
||||
|
||||
### JavaScript Configuration
|
||||
|
||||
The settings are passed from PHP to JavaScript via the `plugininfo.php` class:
|
||||
|
||||
```php
|
||||
protected static function get_autoconvert_configuration(): array {
|
||||
$baseurl = get_config('tiny_mediacms', 'autoconvert_baseurl');
|
||||
|
||||
return [
|
||||
'data' => [
|
||||
'autoConvertEnabled' => (bool) get_config('tiny_mediacms', 'autoconvertenabled'),
|
||||
'autoConvertBaseUrl' => !empty($baseurl) ? $baseurl : '',
|
||||
'autoConvertOptions' => [
|
||||
'showTitle' => (bool) get_config('tiny_mediacms', 'autoconvert_showtitle'),
|
||||
'linkTitle' => (bool) get_config('tiny_mediacms', 'autoconvert_linktitle'),
|
||||
'showRelated' => (bool) get_config('tiny_mediacms', 'autoconvert_showrelated'),
|
||||
'showUserAvatar' => (bool) get_config('tiny_mediacms', 'autoconvert_showuseravatar'),
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
### Default Values (in options.js)
|
||||
|
||||
If PHP settings are not configured, the JavaScript uses these defaults:
|
||||
|
||||
```javascript
|
||||
registerOption(dataName, {
|
||||
processor: 'object',
|
||||
"default": {
|
||||
autoConvertEnabled: true,
|
||||
autoConvertBaseUrl: '', // Empty = allow all MediaCMS domains
|
||||
autoConvertOptions: {
|
||||
showTitle: true,
|
||||
linkTitle: true,
|
||||
showRelated: true,
|
||||
showUserAvatar: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Customization
|
||||
|
||||
### Disabling Auto-Convert
|
||||
|
||||
To disable the feature entirely:
|
||||
1. Go to the plugin settings (see "Accessing Settings" above)
|
||||
2. Uncheck **Enable auto-convert**
|
||||
3. Save changes
|
||||
|
||||
### Programmatic Configuration
|
||||
|
||||
You can also set these values directly in the database using Moodle's `set_config()` function:
|
||||
|
||||
```php
|
||||
// Disable auto-convert
|
||||
set_config('autoconvertenabled', 0, 'tiny_mediacms');
|
||||
|
||||
// Set the MediaCMS base URL (restrict to specific domain)
|
||||
set_config('autoconvert_baseurl', 'https://deic.mediacms.io', 'tiny_mediacms');
|
||||
|
||||
// Customize embed options
|
||||
set_config('autoconvert_showtitle', 1, 'tiny_mediacms');
|
||||
set_config('autoconvert_linktitle', 0, 'tiny_mediacms');
|
||||
set_config('autoconvert_showrelated', 0, 'tiny_mediacms');
|
||||
set_config('autoconvert_showuseravatar', 1, 'tiny_mediacms');
|
||||
```
|
||||
|
||||
### CLI Configuration
|
||||
|
||||
Using Moodle CLI:
|
||||
|
||||
```bash
|
||||
# Enable auto-convert
|
||||
php admin/cli/cfg.php --component=tiny_mediacms --name=autoconvertenabled --set=1
|
||||
|
||||
# Set the MediaCMS base URL
|
||||
php admin/cli/cfg.php --component=tiny_mediacms --name=autoconvert_baseurl --set=https://deic.mediacms.io
|
||||
|
||||
# Disable showing related videos
|
||||
php admin/cli/cfg.php --component=tiny_mediacms --name=autoconvert_showrelated --set=0
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Auto-convert not working
|
||||
|
||||
1. **Check if enabled**: Verify the setting is enabled in plugin settings
|
||||
2. **Clear caches**: Purge all caches (Site administration → Development → Purge all caches)
|
||||
3. **Check URL format**: Ensure the URL matches the pattern `https://[domain]/view?m=[VIDEO_ID]`
|
||||
4. **Browser console**: Check for JavaScript errors in the browser developer console
|
||||
|
||||
### Rebuilding JavaScript
|
||||
|
||||
If you modify the source files, rebuild using:
|
||||
|
||||
```bash
|
||||
cd /path/to/moodle
|
||||
npx grunt amd --root=public/lib/editor/tiny/plugins/mediacms
|
||||
```
|
||||
|
||||
Note: Requires Node.js 22.x or compatible version as specified in Moodle's requirements.
|
||||
|
||||
## Version History
|
||||
|
||||
- **1.0.0** - Initial implementation of auto-convert feature
|
||||
187
lms-plugins/mediacms-moodle/tiny/mediacms/README.md
Normal file
187
lms-plugins/mediacms-moodle/tiny/mediacms/README.md
Normal file
@@ -0,0 +1,187 @@
|
||||
# TinyMCE MediaCMS Plugin for Moodle
|
||||
|
||||
A TinyMCE editor plugin for Moodle that provides media embedding capabilities with MediaCMS/LTI integration.
|
||||
|
||||
## Plugin Information
|
||||
|
||||
- **Component:** `tiny_mediacms`
|
||||
- **Version:** See `version.php`
|
||||
- **Requires:** Moodle 4.5+ (2024100100)
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
mediacms/
|
||||
├── amd/
|
||||
│ ├── src/ # JavaScript source files (ES6 modules)
|
||||
│ │ ├── plugin.js # Main plugin entry point
|
||||
│ │ ├── commands.js # Editor commands
|
||||
│ │ ├── configuration.js # Plugin configuration
|
||||
│ │ ├── iframeembed.js # Iframe embedding logic
|
||||
│ │ ├── iframemodal.js # Iframe modal UI
|
||||
│ │ ├── autoconvert.js # URL auto-conversion
|
||||
│ │ ├── embed.js # Media embedding
|
||||
│ │ ├── embedmodal.js # Embed modal UI
|
||||
│ │ ├── image.js # Image handling
|
||||
│ │ ├── imagemodal.js # Image modal UI
|
||||
│ │ ├── imageinsert.js # Image insertion
|
||||
│ │ ├── imagedetails.js # Image details panel
|
||||
│ │ ├── imagehelpers.js # Image utility functions
|
||||
│ │ ├── manager.js # File manager
|
||||
│ │ ├── options.js # Plugin options
|
||||
│ │ ├── selectors.js # DOM selectors
|
||||
│ │ ├── common.js # Shared utilities
|
||||
│ │ └── usedfiles.js # Track used files
|
||||
│ └── build/ # Compiled/minified files (generated)
|
||||
├── classes/ # PHP classes
|
||||
├── lang/ # Language strings
|
||||
│ └── en/
|
||||
│ └── tiny_mediacms.php
|
||||
├── templates/ # Mustache templates
|
||||
├── styles.css # Plugin styles
|
||||
├── settings.php # Admin settings
|
||||
└── version.php # Plugin version
|
||||
```
|
||||
|
||||
## Building JavaScript (AMD Modules)
|
||||
|
||||
When you modify JavaScript files in `amd/src/`, you must rebuild the minified files in `amd/build/`.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Make sure you have Node.js installed and have run `npm install` in the Moodle root directory:
|
||||
|
||||
```bash
|
||||
cd /path/to/moodle/public
|
||||
npm install
|
||||
```
|
||||
|
||||
### Build Commands
|
||||
|
||||
#### Build all AMD modules (entire Moodle):
|
||||
|
||||
```bash
|
||||
cd /path/to/moodle/public
|
||||
npx grunt amd
|
||||
```
|
||||
|
||||
#### Build only this plugin's AMD modules:
|
||||
|
||||
```bash
|
||||
cd /path/to/moodle/public
|
||||
npx grunt amd --root=lib/editor/tiny/plugins/mediacms
|
||||
```
|
||||
|
||||
#### Watch for changes (auto-rebuild):
|
||||
|
||||
```bash
|
||||
cd /path/to/moodle/public
|
||||
npx grunt watch --root=lib/editor/tiny/plugins/mediacms
|
||||
```
|
||||
|
||||
#### Force build (ignore warnings):
|
||||
|
||||
```bash
|
||||
cd /path/to/moodle/public
|
||||
npx grunt amd --force --root=lib/editor/tiny/plugins/mediacms
|
||||
```
|
||||
|
||||
### Build Output
|
||||
|
||||
After running grunt, the following files are generated in `amd/build/`:
|
||||
|
||||
- `*.min.js` - Minified JavaScript files
|
||||
- `*.min.js.map` - Source maps for debugging
|
||||
|
||||
## Development Mode (Skip Building)
|
||||
|
||||
For faster development, you can skip building by enabling developer mode in Moodle's `config.php`:
|
||||
|
||||
```php
|
||||
// Add these lines to config.php
|
||||
$CFG->debugdeveloper = true;
|
||||
$CFG->cachejs = false;
|
||||
```
|
||||
|
||||
This tells Moodle to load the unminified source files directly from `amd/src/` instead of `amd/build/`.
|
||||
|
||||
**Note:** Always build before committing or deploying to production!
|
||||
|
||||
## Purging Caches
|
||||
|
||||
After making changes, you may need to purge Moodle caches:
|
||||
|
||||
### Via CLI (Docker):
|
||||
|
||||
```bash
|
||||
docker compose exec moodle php /var/www/html/public/admin/cli/purge_caches.php
|
||||
```
|
||||
|
||||
### Via CLI (Local):
|
||||
|
||||
```bash
|
||||
php admin/cli/purge_caches.php
|
||||
```
|
||||
|
||||
### Via Web:
|
||||
|
||||
Visit: `http://your-moodle-site/admin/purgecaches.php`
|
||||
|
||||
## What Needs Cache Purging?
|
||||
|
||||
| File Type | Cache Purge Needed? |
|
||||
|-----------|---------------------|
|
||||
| `amd/src/*.js` | No (if `$CFG->cachejs = false`) |
|
||||
| `amd/build/*.min.js` | Yes |
|
||||
| `lang/en/*.php` | Yes |
|
||||
| `templates/*.mustache` | Yes |
|
||||
| `styles.css` | Yes |
|
||||
| `classes/*.php` | Usually no |
|
||||
| `settings.php` | Yes |
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Changes not appearing?
|
||||
|
||||
1. **JavaScript changes:**
|
||||
- Rebuild AMD modules: `npx grunt amd --root=lib/editor/tiny/plugins/mediacms`
|
||||
- Hard refresh browser: `Cmd+Shift+R` (Mac) / `Ctrl+Shift+R` (Windows/Linux)
|
||||
- Check browser console for errors
|
||||
|
||||
2. **Language strings:**
|
||||
- Purge Moodle caches
|
||||
|
||||
3. **Templates:**
|
||||
- Purge Moodle caches
|
||||
|
||||
4. **Styles:**
|
||||
- Purge Moodle caches
|
||||
- Hard refresh browser
|
||||
|
||||
### Grunt errors?
|
||||
|
||||
```bash
|
||||
# Make sure dependencies are installed
|
||||
cd /path/to/moodle/public
|
||||
npm install
|
||||
|
||||
# Try with force flag
|
||||
npx grunt amd --force --root=lib/editor/tiny/plugins/mediacms
|
||||
```
|
||||
|
||||
### ESLint errors?
|
||||
|
||||
Fix linting issues or use:
|
||||
|
||||
```bash
|
||||
npx grunt amd --force --root=lib/editor/tiny/plugins/mediacms
|
||||
```
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [AUTOCONVERT.md](./AUTOCONVERT.md) - URL auto-conversion feature documentation
|
||||
- [LTI_INTEGRATION.md](./LTI_INTEGRATION.md) - LTI integration documentation
|
||||
|
||||
## License
|
||||
|
||||
GNU GPL v3 or later - http://www.gnu.org/copyleft/gpl.html
|
||||
15
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/autoconvert.min.js
vendored
Executable file
15
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/autoconvert.min.js
vendored
Executable file
@@ -0,0 +1,15 @@
|
||||
define("tiny_mediacms/autoconvert",["exports","./options"],(function(_exports,_options){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.setupAutoConvert=_exports.isMediaCMSUrl=_exports.convertToEmbed=void 0;
|
||||
/**
|
||||
* Tiny MediaCMS Auto-convert module.
|
||||
*
|
||||
* This module automatically converts pasted MediaCMS URLs into embedded videos.
|
||||
* When a user pastes a MediaCMS video URL (e.g., https://deic.mediacms.io/view?m=JpBd1Zvdl),
|
||||
* it will be automatically converted to an iframe embed.
|
||||
*
|
||||
* @module tiny_mediacms/autoconvert
|
||||
* @copyright 2024
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
const MEDIACMS_VIEW_URL_PATTERN=/^(https?:\/\/[^\/]+)\/view\?m=([a-zA-Z0-9_-]+)$/,parseMediaCMSUrl=text=>{if(!text||"string"!=typeof text)return null;const trimmed=text.trim(),match=trimmed.match(MEDIACMS_VIEW_URL_PATTERN);return match?{baseUrl:match[1],videoId:match[2],originalUrl:trimmed}:null},isDomainAllowed=(parsed,config)=>{const configuredBaseUrl=config.autoConvertBaseUrl||config.mediacmsBaseUrl;if(!configuredBaseUrl)return!0;try{const configuredUrl=new URL(configuredBaseUrl),pastedUrl=new URL(parsed.baseUrl);return configuredUrl.host===pastedUrl.host}catch(e){return!0}},generateEmbedHtml=function(parsed){let options=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const embedUrl=new URL("".concat(parsed.baseUrl,"/embed"));embedUrl.searchParams.set("m",parsed.videoId),embedUrl.searchParams.set("showTitle",!1!==options.showTitle?"1":"0"),embedUrl.searchParams.set("showRelated",!1!==options.showRelated?"1":"0"),embedUrl.searchParams.set("showUserAvatar",!1!==options.showUserAvatar?"1":"0"),embedUrl.searchParams.set("linkTitle",!1!==options.linkTitle?"1":"0");const html='<iframe src="'.concat(embedUrl.toString(),'" ')+'style="width: 100%; aspect-ratio: 16 / 9; display: block; border: 0;" allowfullscreen="allowfullscreen"></iframe>';return html};_exports.setupAutoConvert=editor=>{const config=(0,_options.getData)(editor)||{};!1!==config.autoConvertEnabled&&(editor.on("paste",(e=>{handlePasteEvent(editor,e,config)})),editor.on("input",(e=>{handleInputEvent(editor,e,config)})))};const handlePasteEvent=(editor,e,config)=>{const clipboardData=e.clipboardData||window.clipboardData;if(!clipboardData)return;const text=clipboardData.getData("text/plain")||clipboardData.getData("text");if(!text)return;const parsed=parseMediaCMSUrl(text);if(!parsed)return;if(!isDomainAllowed(parsed,config))return;e.preventDefault(),e.stopPropagation();const embedHtml=generateEmbedHtml(parsed,config.autoConvertOptions||{});setTimeout((()=>{editor.insertContent(embedHtml),editor.selection.collapse(!1)}),0)},handleInputEvent=(editor,e,config)=>{if("insertFromPaste"!==e.inputType&&"insertText"!==e.inputType)return;const node=editor.selection.getNode();if(!node||"P"!==node.nodeName)return;const text=node.textContent||"",parsed=parseMediaCMSUrl(text);if(!parsed||!isDomainAllowed(parsed,config))return;const trimmedHtml=node.innerHTML.trim();if(trimmedHtml!==text.trim()&&!trimmedHtml.startsWith(text.trim()))return;const embedHtml=generateEmbedHtml(parsed,config.autoConvertOptions||{});setTimeout((()=>{const currentText=node.textContent||"",currentParsed=parseMediaCMSUrl(currentText);currentParsed&¤tParsed.originalUrl===parsed.originalUrl&&(editor.selection.select(node),editor.insertContent(embedHtml))}),100)};_exports.isMediaCMSUrl=text=>null!==parseMediaCMSUrl(text);_exports.convertToEmbed=function(url){let options=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const parsed=parseMediaCMSUrl(url);return parsed?generateEmbedHtml(parsed,options):null}}));
|
||||
|
||||
//# sourceMappingURL=autoconvert.min.js.map
|
||||
File diff suppressed because one or more lines are too long
10
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/commands.min.js
vendored
Executable file
10
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/commands.min.js
vendored
Executable file
File diff suppressed because one or more lines are too long
1
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/commands.min.js.map
Executable file
1
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/commands.min.js.map
Executable file
File diff suppressed because one or more lines are too long
3
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/common.min.js
vendored
Executable file
3
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/common.min.js
vendored
Executable file
@@ -0,0 +1,3 @@
|
||||
define("tiny_mediacms/common",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;return _exports.default={pluginName:"tiny_mediacms/plugin",component:"tiny_mediacms",iframeButtonName:"tiny_mediacms_iframe",iframeMenuItemName:"tiny_mediacms_iframe",iframeIcon:"tiny_mediacms_iframe"},_exports.default}));
|
||||
|
||||
//# sourceMappingURL=common.min.js.map
|
||||
1
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/common.min.js.map
Executable file
1
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/common.min.js.map
Executable file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"common.min.js","sources":["../src/common.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Tiny Media common values.\n *\n * @module tiny_mediacms/common\n * @copyright 2022 Huong Nguyen <huongnv13@gmail.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nexport default {\n pluginName: 'tiny_mediacms/plugin',\n component: 'tiny_mediacms',\n iframeButtonName: 'tiny_mediacms_iframe',\n iframeMenuItemName: 'tiny_mediacms_iframe',\n iframeIcon: 'tiny_mediacms_iframe',\n};\n"],"names":["pluginName","component","iframeButtonName","iframeMenuItemName","iframeIcon"],"mappings":"sKAuBe,CACXA,WAAY,uBACZC,UAAW,gBACXC,iBAAkB,uBAClBC,mBAAoB,uBACpBC,WAAY"}
|
||||
3
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/configuration.min.js
vendored
Executable file
3
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/configuration.min.js
vendored
Executable file
@@ -0,0 +1,3 @@
|
||||
define("tiny_mediacms/configuration",["exports","./common","editor_tiny/utils"],(function(_exports,_common,_utils){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.configure=void 0;_exports.configure=instanceConfig=>{return{contextmenu:(0,_utils.addContextmenuItem)(instanceConfig.contextmenu,_common.iframeButtonName),menu:(menu=instanceConfig.menu,menu.insert.items="".concat(_common.iframeMenuItemName," ").concat(menu.insert.items),menu),toolbar:(toolbar=instanceConfig.toolbar,toolbar.map((section=>("content"===section.name&§ion.items.unshift(_common.iframeButtonName),section))))};var toolbar,menu}}));
|
||||
|
||||
//# sourceMappingURL=configuration.min.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"configuration.min.js","sources":["../src/configuration.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Tiny Media configuration.\n *\n * @module tiny_mediacms/configuration\n * @copyright 2022 Huong Nguyen <huongnv13@gmail.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {\n iframeButtonName,\n iframeMenuItemName,\n} from './common';\nimport {\n addContextmenuItem,\n} from 'editor_tiny/utils';\n\nconst configureMenu = (menu) => {\n // Add the Iframe Embed to the insert menu.\n menu.insert.items = `${iframeMenuItemName} ${menu.insert.items}`;\n\n return menu;\n};\n\nconst configureToolbar = (toolbar) => {\n // The toolbar contains an array of named sections.\n // The Moodle integration ensures that there is a section called 'content'.\n\n return toolbar.map((section) => {\n if (section.name === 'content') {\n // Insert the iframe button at the start of it.\n section.items.unshift(iframeButtonName);\n }\n\n return section;\n });\n};\n\nexport const configure = (instanceConfig) => {\n // Update the instance configuration to add the Iframe Embed menu option to the menus and toolbars.\n return {\n contextmenu: addContextmenuItem(instanceConfig.contextmenu, iframeButtonName),\n menu: configureMenu(instanceConfig.menu),\n toolbar: configureToolbar(instanceConfig.toolbar),\n };\n};\n"],"names":["instanceConfig","contextmenu","iframeButtonName","menu","insert","items","iframeMenuItemName","toolbar","map","section","name","unshift"],"mappings":"wNAoD0BA,uBAEf,CACHC,aAAa,6BAAmBD,eAAeC,YAAaC,0BAC5DC,MAzBeA,KAyBKH,eAAeG,KAvBvCA,KAAKC,OAAOC,gBAAWC,uCAAsBH,KAAKC,OAAOC,OAElDF,MAsBHI,SAnBkBA,QAmBQP,eAAeO,QAftCA,QAAQC,KAAKC,UACK,YAAjBA,QAAQC,MAERD,QAAQJ,MAAMM,QAAQT,0BAGnBO,aAVWF,IAAAA,QAPHJ"}
|
||||
3
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/embed.min.js
vendored
Executable file
3
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/embed.min.js
vendored
Executable file
File diff suppressed because one or more lines are too long
1
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/embed.min.js.map
Executable file
1
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/embed.min.js.map
Executable file
File diff suppressed because one or more lines are too long
3
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/embedmodal.min.js
vendored
Executable file
3
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/embedmodal.min.js
vendored
Executable file
@@ -0,0 +1,3 @@
|
||||
define("tiny_mediacms/embedmodal",["exports","core/modal","./common"],(function(_exports,_modal,_common){var obj;function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_modal=(obj=_modal)&&obj.__esModule?obj:{default:obj};class EmbedModal extends _modal.default{registerEventListeners(){super.registerEventListeners(),this.registerCloseOnSave(),this.registerCloseOnCancel()}configure(modalConfig){modalConfig.large=!0,modalConfig.removeOnClose=!0,modalConfig.show=!0,super.configure(modalConfig)}}return _exports.default=EmbedModal,_defineProperty(EmbedModal,"TYPE","".concat(_common.component,"/modal")),_defineProperty(EmbedModal,"TEMPLATE","".concat(_common.component,"/embed_media_modal")),_exports.default}));
|
||||
|
||||
//# sourceMappingURL=embedmodal.min.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"embedmodal.min.js","sources":["../src/embedmodal.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Embedded Media Management Modal for Tiny.\n *\n * @module tiny_mediacms/embedmodal\n * @copyright 2022 Andrew Lyons <andrew@nicols.co.uk>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Modal from 'core/modal';\nimport {component} from './common';\n\nexport default class EmbedModal extends Modal {\n static TYPE = `${component}/modal`;\n static TEMPLATE = `${component}/embed_media_modal`;\n\n registerEventListeners() {\n // Call the parent registration.\n super.registerEventListeners();\n\n // Register to close on save/cancel.\n this.registerCloseOnSave();\n this.registerCloseOnCancel();\n }\n\n configure(modalConfig) {\n modalConfig.large = true;\n modalConfig.removeOnClose = true;\n modalConfig.show = true;\n\n super.configure(modalConfig);\n }\n}\n"],"names":["EmbedModal","Modal","registerEventListeners","registerCloseOnSave","registerCloseOnCancel","configure","modalConfig","large","removeOnClose","show","component"],"mappings":"iaA0BqBA,mBAAmBC,eAIpCC,+BAEUA,8BAGDC,2BACAC,wBAGTC,UAAUC,aACNA,YAAYC,OAAQ,EACpBD,YAAYE,eAAgB,EAC5BF,YAAYG,MAAO,QAEbJ,UAAUC,iEAlBHN,4BACAU,6CADAV,gCAEIU"}
|
||||
3
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/iframeembed.min.js
vendored
Executable file
3
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/iframeembed.min.js
vendored
Executable file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
3
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/iframemodal.min.js
vendored
Executable file
3
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/iframemodal.min.js
vendored
Executable file
@@ -0,0 +1,3 @@
|
||||
define("tiny_mediacms/iframemodal",["exports","core/modal","./common"],(function(_exports,_modal,_common){var obj;function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_modal=(obj=_modal)&&obj.__esModule?obj:{default:obj};class IframeModal extends _modal.default{registerEventListeners(){super.registerEventListeners(),this.registerCloseOnSave(),this.registerCloseOnCancel()}configure(modalConfig){modalConfig.large=!0,modalConfig.removeOnClose=!0,modalConfig.show=!0,super.configure(modalConfig)}}return _exports.default=IframeModal,_defineProperty(IframeModal,"TYPE","".concat(_common.component,"/iframemodal")),_defineProperty(IframeModal,"TEMPLATE","".concat(_common.component,"/iframe_embed_modal")),_exports.default}));
|
||||
|
||||
//# sourceMappingURL=iframemodal.min.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"iframemodal.min.js","sources":["../src/iframemodal.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Iframe Embed Modal for Tiny Media2.\n *\n * @module tiny_mediacms/iframemodal\n * @copyright 2024\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Modal from 'core/modal';\nimport {component} from './common';\n\nexport default class IframeModal extends Modal {\n static TYPE = `${component}/iframemodal`;\n static TEMPLATE = `${component}/iframe_embed_modal`;\n\n registerEventListeners() {\n // Call the parent registration.\n super.registerEventListeners();\n\n // Register to close on save/cancel.\n this.registerCloseOnSave();\n this.registerCloseOnCancel();\n }\n\n configure(modalConfig) {\n modalConfig.large = true;\n modalConfig.removeOnClose = true;\n modalConfig.show = true;\n\n super.configure(modalConfig);\n }\n}\n"],"names":["IframeModal","Modal","registerEventListeners","registerCloseOnSave","registerCloseOnCancel","configure","modalConfig","large","removeOnClose","show","component"],"mappings":"kaA0BqBA,oBAAoBC,eAIrCC,+BAEUA,8BAGDC,2BACAC,wBAGTC,UAAUC,aACNA,YAAYC,OAAQ,EACpBD,YAAYE,eAAgB,EAC5BF,YAAYG,MAAO,QAEbJ,UAAUC,kEAlBHN,6BACAU,mDADAV,iCAEIU"}
|
||||
3
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/image.min.js
vendored
Executable file
3
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/image.min.js
vendored
Executable file
File diff suppressed because one or more lines are too long
1
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/image.min.js.map
Executable file
1
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/image.min.js.map
Executable file
File diff suppressed because one or more lines are too long
3
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/imagedetails.min.js
vendored
Executable file
3
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/imagedetails.min.js
vendored
Executable file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
10
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/imagehelpers.min.js
vendored
Executable file
10
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/imagehelpers.min.js
vendored
Executable file
@@ -0,0 +1,10 @@
|
||||
define("tiny_mediacms/imagehelpers",["exports","core/templates"],(function(_exports,_templates){var obj;
|
||||
/**
|
||||
* Tiny media plugin image helpers.
|
||||
*
|
||||
* @module tiny_mediacms/imagehelpers
|
||||
* @copyright 2024 Meirza <meirza.arson@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.showElements=_exports.isPercentageValue=_exports.hideElements=_exports.footerImageInsert=_exports.footerImageDetails=_exports.bodyImageInsert=_exports.bodyImageDetails=void 0,_templates=(obj=_templates)&&obj.__esModule?obj:{default:obj};_exports.bodyImageInsert=async(templateContext,root)=>_templates.default.renderForPromise("tiny_mediacms/insert_image_modal_insert",{...templateContext}).then((_ref=>{let{html:html,js:js}=_ref;_templates.default.replaceNodeContents(root.querySelector(".tiny_imagecms_body_template"),html,js)})).catch((error=>{window.console.log(error)}));_exports.footerImageInsert=async(templateContext,root)=>_templates.default.renderForPromise("tiny_mediacms/insert_image_modal_insert_footer",{...templateContext}).then((_ref2=>{let{html:html,js:js}=_ref2;_templates.default.replaceNodeContents(root.querySelector(".tiny_imagecms_footer_template"),html,js)})).catch((error=>{window.console.log(error)}));_exports.bodyImageDetails=async(templateContext,root)=>_templates.default.renderForPromise("tiny_mediacms/insert_image_modal_details",{...templateContext}).then((_ref3=>{let{html:html,js:js}=_ref3;_templates.default.replaceNodeContents(root.querySelector(".tiny_imagecms_body_template"),html,js)})).catch((error=>{window.console.log(error)}));_exports.footerImageDetails=async(templateContext,root)=>_templates.default.renderForPromise("tiny_mediacms/insert_image_modal_details_footer",{...templateContext}).then((_ref4=>{let{html:html,js:js}=_ref4;_templates.default.replaceNodeContents(root.querySelector(".tiny_imagecms_footer_template"),html,js)})).catch((error=>{window.console.log(error)}));_exports.showElements=(elements,root)=>{if(elements instanceof Array)elements.forEach((elementSelector=>{const element=root.querySelector(elementSelector);element&&element.classList.remove("d-none")}));else{const element=root.querySelector(elements);element&&element.classList.remove("d-none")}};_exports.hideElements=(elements,root)=>{if(elements instanceof Array)elements.forEach((elementSelector=>{const element=root.querySelector(elementSelector);element&&element.classList.add("d-none")}));else{const element=root.querySelector(elements);element&&element.classList.add("d-none")}};_exports.isPercentageValue=value=>value.match(/\d+%/)}));
|
||||
|
||||
//# sourceMappingURL=imagehelpers.min.js.map
|
||||
File diff suppressed because one or more lines are too long
3
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/imageinsert.min.js
vendored
Executable file
3
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/imageinsert.min.js
vendored
Executable file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
3
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/imagemodal.min.js
vendored
Executable file
3
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/imagemodal.min.js
vendored
Executable file
@@ -0,0 +1,3 @@
|
||||
define("tiny_mediacms/imagemodal",["exports","core/modal","./common"],(function(_exports,_modal,_common){var obj;function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_modal=(obj=_modal)&&obj.__esModule?obj:{default:obj};class ImageModal extends _modal.default{registerEventListeners(){super.registerEventListeners(),this.registerCloseOnSave(),this.registerCloseOnCancel()}configure(modalConfig){modalConfig.large=!0,modalConfig.removeOnClose=!0,modalConfig.show=!0,super.configure(modalConfig)}}return _exports.default=ImageModal,_defineProperty(ImageModal,"TYPE","".concat(_common.component,"/imagemodal")),_defineProperty(ImageModal,"TEMPLATE","".concat(_common.component,"/insert_image_modal")),ImageModal.registerModalType(),_exports.default}));
|
||||
|
||||
//# sourceMappingURL=imagemodal.min.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"imagemodal.min.js","sources":["../src/imagemodal.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Image Modal for Tiny.\n *\n * @module tiny_mediacms/imagemodal\n * @copyright 2022 Huong Nguyen <huongnv13@gmail.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Modal from 'core/modal';\nimport {component} from './common';\n\nexport default class ImageModal extends Modal {\n static TYPE = `${component}/imagemodal`;\n static TEMPLATE = `${component}/insert_image_modal`;\n\n registerEventListeners() {\n // Call the parent registration.\n super.registerEventListeners();\n\n // Register to close on save/cancel.\n this.registerCloseOnSave();\n this.registerCloseOnCancel();\n }\n\n configure(modalConfig) {\n modalConfig.large = true;\n modalConfig.removeOnClose = true;\n modalConfig.show = true;\n\n super.configure(modalConfig);\n }\n}\n\nImageModal.registerModalType();\n"],"names":["ImageModal","Modal","registerEventListeners","registerCloseOnSave","registerCloseOnCancel","configure","modalConfig","large","removeOnClose","show","component","registerModalType"],"mappings":"iaA0BqBA,mBAAmBC,eAIpCC,+BAEUA,8BAGDC,2BACAC,wBAGTC,UAAUC,aACNA,YAAYC,OAAQ,EACpBD,YAAYE,eAAgB,EAC5BF,YAAYG,MAAO,QAEbJ,UAAUC,iEAlBHN,4BACAU,kDADAV,gCAEIU,0CAoBzBV,WAAWW"}
|
||||
3
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/manager.min.js
vendored
Executable file
3
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/manager.min.js
vendored
Executable file
@@ -0,0 +1,3 @@
|
||||
define("tiny_mediacms/manager",["exports","core/templates","core/str","core/modal","core/modal_events","./options","core/config"],(function(_exports,_templates,_str,_modal,ModalEvents,_options,_config){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_templates=_interopRequireDefault(_templates),_modal=_interopRequireDefault(_modal),ModalEvents=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(ModalEvents),_config=_interopRequireDefault(_config);return _exports.default=class{constructor(editor){_defineProperty(this,"editor",null),_defineProperty(this,"area",null),this.editor=editor;const data=(0,_options.getData)(editor);this.area=data.params.area,this.area.itemid=data.fpoptions.image.itemid}async displayDialogue(){const modal=await _modal.default.create({large:!0,title:(0,_str.getString)("mediamanagerproperties","tiny_mediacms"),body:_templates.default.render("tiny_mediacms/mm2_iframe",{src:this.getIframeURL()}),removeOnClose:!0,show:!0});return modal.getRoot().on(ModalEvents.bodyRendered,(()=>{this.selectFirstElement()})),document.querySelector(".modal-lg").style.cssText="max-width: 850px",modal}selectFirstElement(){const iframe=document.getElementById("mm2-iframe");iframe.addEventListener("load",(function(){let intervalId=setInterval((function(){const iDocument=iframe.contentWindow.document;if(iDocument.querySelector(".filemanager")){const firstFocusableElement=iDocument.querySelector(".fp-navbar a:not([disabled])");firstFocusableElement&&firstFocusableElement.focus(),clearInterval(intervalId)}}),200)}))}getIframeURL(){const url=new URL("".concat(_config.default.wwwroot,"/lib/editor/tiny/plugins/mediacms/manage.php"));url.searchParams.append("elementid",this.editor.getElement().id);for(const key in this.area)url.searchParams.append(key,this.area[key]);return url.toString()}},_exports.default}));
|
||||
|
||||
//# sourceMappingURL=manager.min.js.map
|
||||
1
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/manager.min.js.map
Executable file
1
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/manager.min.js.map
Executable file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"manager.min.js","sources":["../src/manager.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Tiny Media Manager plugin class for Moodle.\n *\n * @module tiny_mediacms/manager\n * @copyright 2022, Stevani Andolo <stevani@hotmail.com.au>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Templates from 'core/templates';\nimport {getString} from 'core/str';\nimport Modal from 'core/modal';\nimport * as ModalEvents from 'core/modal_events';\nimport {getData} from './options';\nimport Config from 'core/config';\n\nexport default class MediaManager {\n\n editor = null;\n area = null;\n\n constructor(editor) {\n this.editor = editor;\n const data = getData(editor);\n this.area = data.params.area;\n this.area.itemid = data.fpoptions.image.itemid;\n }\n\n async displayDialogue() {\n const modal = await Modal.create({\n large: true,\n title: getString('mediamanagerproperties', 'tiny_mediacms'),\n body: Templates.render('tiny_mediacms/mm2_iframe', {\n src: this.getIframeURL()\n }),\n removeOnClose: true,\n show: true,\n });\n modal.getRoot().on(ModalEvents.bodyRendered, () => {\n this.selectFirstElement();\n });\n\n document.querySelector('.modal-lg').style.cssText = `max-width: 850px`;\n return modal;\n }\n\n // It will select the first element in the file manager.\n selectFirstElement() {\n const iframe = document.getElementById('mm2-iframe');\n iframe.addEventListener('load', function() {\n let intervalId = setInterval(function() {\n const iDocument = iframe.contentWindow.document;\n if (iDocument.querySelector('.filemanager')) {\n const firstFocusableElement = iDocument.querySelector('.fp-navbar a:not([disabled])');\n if (firstFocusableElement) {\n firstFocusableElement.focus();\n }\n clearInterval(intervalId);\n }\n }, 200);\n });\n }\n\n getIframeURL() {\n const url = new URL(`${Config.wwwroot}/lib/editor/tiny/plugins/mediacms/manage.php`);\n url.searchParams.append('elementid', this.editor.getElement().id);\n for (const key in this.area) {\n url.searchParams.append(key, this.area[key]);\n }\n return url.toString();\n }\n}\n"],"names":["constructor","editor","data","area","params","itemid","fpoptions","image","modal","Modal","create","large","title","body","Templates","render","src","this","getIframeURL","removeOnClose","show","getRoot","on","ModalEvents","bodyRendered","selectFirstElement","document","querySelector","style","cssText","iframe","getElementById","addEventListener","intervalId","setInterval","iDocument","contentWindow","firstFocusableElement","focus","clearInterval","url","URL","Config","wwwroot","searchParams","append","getElement","id","key","toString"],"mappings":"mmDAmCIA,YAAYC,sCAHH,kCACF,WAGEA,OAASA,aACRC,MAAO,oBAAQD,aAChBE,KAAOD,KAAKE,OAAOD,UACnBA,KAAKE,OAASH,KAAKI,UAAUC,MAAMF,qCAIlCG,YAAcC,eAAMC,OAAO,CAC7BC,OAAO,EACPC,OAAO,kBAAU,yBAA0B,iBAC3CC,KAAMC,mBAAUC,OAAO,2BAA4B,CAC/CC,IAAKC,KAAKC,iBAEdC,eAAe,EACfC,MAAM,WAEVZ,MAAMa,UAAUC,GAAGC,YAAYC,cAAc,UACpCC,wBAGTC,SAASC,cAAc,aAAaC,MAAMC,2BACnCrB,MAIXiB,2BACUK,OAASJ,SAASK,eAAe,cACvCD,OAAOE,iBAAiB,QAAQ,eACxBC,WAAaC,aAAY,iBACnBC,UAAYL,OAAOM,cAAcV,YACnCS,UAAUR,cAAc,gBAAiB,OACnCU,sBAAwBF,UAAUR,cAAc,gCAClDU,uBACAA,sBAAsBC,QAE1BC,cAAcN,eAEnB,QAIXf,qBACUsB,IAAM,IAAIC,cAAOC,gBAAOC,yDAC9BH,IAAII,aAAaC,OAAO,YAAa5B,KAAKhB,OAAO6C,aAAaC,QACzD,MAAMC,OAAO/B,KAAKd,KACnBqC,IAAII,aAAaC,OAAOG,IAAK/B,KAAKd,KAAK6C,aAEpCR,IAAIS"}
|
||||
11
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/options.min.js
vendored
Executable file
11
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/options.min.js
vendored
Executable file
@@ -0,0 +1,11 @@
|
||||
define("tiny_mediacms/options",["exports","editor_tiny/options","./common"],(function(_exports,_options,_common){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.register=_exports.getPermissions=_exports.getLti=_exports.getImagePermissions=_exports.getEmbedPermissions=_exports.getData=void 0;
|
||||
/**
|
||||
* Options helper for Tiny Media plugin.
|
||||
*
|
||||
* @module tiny_mediacms/options
|
||||
* @copyright 2022 Huong Nguyen <huongnv13@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
const dataName=(0,_options.getPluginOptionName)(_common.pluginName,"data"),permissionsName=(0,_options.getPluginOptionName)(_common.pluginName,"permissions"),ltiName=(0,_options.getPluginOptionName)(_common.pluginName,"lti");_exports.register=editor=>{const registerOption=editor.options.register;registerOption(permissionsName,{processor:"object",default:{image:{filepicker:!1}}}),registerOption(dataName,{processor:"object",default:{mediacmsApiUrl:"",mediacmsBaseUrl:"",mediacmsPageSize:12,autoConvertEnabled:!0,autoConvertBaseUrl:"",autoConvertOptions:{showTitle:!0,linkTitle:!0,showRelated:!0,showUserAvatar:!0}}}),registerOption(ltiName,{processor:"object",default:{toolId:0,courseId:0,contentItemUrl:""}})};const getPermissions=editor=>editor.options.get(permissionsName);_exports.getPermissions=getPermissions;_exports.getImagePermissions=editor=>getPermissions(editor).image;_exports.getEmbedPermissions=editor=>getPermissions(editor).embed;_exports.getData=editor=>editor.options.get(dataName);_exports.getLti=editor=>editor.options.get(ltiName)}));
|
||||
|
||||
//# sourceMappingURL=options.min.js.map
|
||||
1
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/options.min.js.map
Executable file
1
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/options.min.js.map
Executable file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"options.min.js","sources":["../src/options.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Options helper for Tiny Media plugin.\n *\n * @module tiny_mediacms/options\n * @copyright 2022 Huong Nguyen <huongnv13@gmail.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {getPluginOptionName} from 'editor_tiny/options';\nimport {pluginName} from './common';\n\nconst dataName = getPluginOptionName(pluginName, 'data');\nconst permissionsName = getPluginOptionName(pluginName, 'permissions');\nconst ltiName = getPluginOptionName(pluginName, 'lti');\n\n/**\n * Register the options for the Tiny Media plugin.\n *\n * @param {TinyMCE} editor\n */\nexport const register = (editor) => {\n const registerOption = editor.options.register;\n\n registerOption(permissionsName, {\n processor: 'object',\n \"default\": {\n image: {\n filepicker: false,\n }\n },\n });\n\n registerOption(dataName, {\n processor: 'object',\n \"default\": {\n // MediaCMS video library configuration\n mediacmsApiUrl: '', // e.g., 'https://deic.mediacms.io/api/v1/media'\n mediacmsBaseUrl: '', // e.g., 'https://deic.mediacms.io'\n mediacmsPageSize: 12,\n // Auto-conversion settings\n autoConvertEnabled: true, // Enable/disable auto-conversion of pasted MediaCMS URLs\n autoConvertBaseUrl: '', // Base URL to restrict auto-conversion (empty = allow all MediaCMS domains)\n autoConvertOptions: {\n // Default embed options for auto-converted videos\n showTitle: true,\n linkTitle: true,\n showRelated: true,\n showUserAvatar: true,\n },\n },\n });\n\n registerOption(ltiName, {\n processor: 'object',\n \"default\": {\n // LTI configuration for MediaCMS iframe library\n toolId: 0, // LTI external tool ID\n courseId: 0, // Current course ID\n contentItemUrl: '', // URL to /mod/lti/contentitem.php for Deep Linking\n },\n });\n};\n\n/**\n * Get the permissions configuration for the Tiny Media plugin.\n *\n * @param {TinyMCE} editor\n * @returns {object}\n */\nexport const getPermissions = (editor) => editor.options.get(permissionsName);\n\n/**\n * Get the permissions configuration for the Tiny Media plugin.\n *\n * @param {TinyMCE} editor\n * @returns {object}\n */\nexport const getImagePermissions = (editor) => getPermissions(editor).image;\n\n/**\n * Get the permissions configuration for the Tiny Media plugin.\n *\n * @param {TinyMCE} editor\n * @returns {object}\n */\nexport const getEmbedPermissions = (editor) => getPermissions(editor).embed;\n\n/**\n * Get the data configuration for the Media Manager.\n *\n * @param {TinyMCE} editor\n * @returns {object}\n */\nexport const getData = (editor) => editor.options.get(dataName);\n\n/**\n * Get the LTI configuration for the MediaCMS iframe library.\n *\n * @param {TinyMCE} editor\n * @returns {object}\n */\nexport const getLti = (editor) => editor.options.get(ltiName);\n"],"names":["dataName","pluginName","permissionsName","ltiName","editor","registerOption","options","register","processor","image","filepicker","mediacmsApiUrl","mediacmsBaseUrl","mediacmsPageSize","autoConvertEnabled","autoConvertBaseUrl","autoConvertOptions","showTitle","linkTitle","showRelated","showUserAvatar","toolId","courseId","contentItemUrl","getPermissions","get","embed"],"mappings":";;;;;;;;MA0BMA,UAAW,gCAAoBC,mBAAY,QAC3CC,iBAAkB,gCAAoBD,mBAAY,eAClDE,SAAU,gCAAoBF,mBAAY,yBAOvBG,eACfC,eAAiBD,OAAOE,QAAQC,SAEtCF,eAAeH,gBAAiB,CAC5BM,UAAW,iBACA,CACPC,MAAO,CACHC,YAAY,MAKxBL,eAAeL,SAAU,CACrBQ,UAAW,iBACA,CAEPG,eAAgB,GAChBC,gBAAiB,GACjBC,iBAAkB,GAElBC,oBAAoB,EACpBC,mBAAoB,GACpBC,mBAAoB,CAEhBC,WAAW,EACXC,WAAW,EACXC,aAAa,EACbC,gBAAgB,MAK5Bf,eAAeF,QAAS,CACpBK,UAAW,iBACA,CAEPa,OAAQ,EACRC,SAAU,EACVC,eAAgB,aAWfC,eAAkBpB,QAAWA,OAAOE,QAAQmB,IAAIvB,qFAQzBE,QAAWoB,eAAepB,QAAQK,mCAQlCL,QAAWoB,eAAepB,QAAQsB,uBAQ9CtB,QAAWA,OAAOE,QAAQmB,IAAIzB,0BAQ/BI,QAAWA,OAAOE,QAAQmB,IAAItB"}
|
||||
10
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/plugin.min.js
vendored
Executable file
10
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/plugin.min.js
vendored
Executable file
@@ -0,0 +1,10 @@
|
||||
define("tiny_mediacms/plugin",["exports","editor_tiny/loader","editor_tiny/utils","./common","./commands","./configuration","./options","./autoconvert"],(function(_exports,_loader,_utils,_common,Commands,Configuration,Options,_autoconvert){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}
|
||||
/**
|
||||
* Tiny Media plugin for Moodle.
|
||||
*
|
||||
* @module tiny_mediacms/plugin
|
||||
* @copyright 2022 Andrew Lyons <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,Commands=_interopRequireWildcard(Commands),Configuration=_interopRequireWildcard(Configuration),Options=_interopRequireWildcard(Options);const isMediaCMSUrl=url=>{if(!url)return!1;try{const urlObj=new URL(url);return("/embed"===urlObj.pathname||"/view"===urlObj.pathname)&&urlObj.searchParams.has("m")}catch(e){return!1}},MEDIACMS_URL_PATTERN=/(^|>|\s)(https?:\/\/[^\s<>"]+\/(?:embed|view)\?m=[^\s<>"]+)(<|\s|$)/g;var _default=new Promise((async resolve=>{const[tinyMCE,setupCommands,pluginMetadata]=await Promise.all([(0,_loader.getTinyMCE)(),Commands.getSetup(),(0,_utils.getPluginMetadata)(_common.component,_common.pluginName)]);tinyMCE.PluginManager.add("".concat(_common.component,"/plugin"),(editor=>(Options.register(editor),setupCommands(editor),(0,_autoconvert.setupAutoConvert)(editor),editor.on("BeforeSetContent",(e=>{e.content&&"string"==typeof e.content&&(e.content=e.content.replace(MEDIACMS_URL_PATTERN,((match,before,url,after)=>isMediaCMSUrl(url)?before+(url=>{let embedUrl=url;try{const urlObj=new URL(url);"/view"===urlObj.pathname&&(urlObj.pathname="/embed",embedUrl=urlObj.toString())}catch(e){}return'<iframe src="'.concat(embedUrl,'" ')+'style="width: 100%; aspect-ratio: 16 / 9; display: block; border: 0;" allowfullscreen="allowfullscreen"></iframe>'})(url)+after:match)))})),editor.on("GetContent",(e=>{if("html"===e.format){const tempDiv=document.createElement("div");tempDiv.innerHTML=e.content,tempDiv.querySelectorAll(".tiny-mediacms-edit-btn").forEach((btn=>btn.remove())),tempDiv.querySelectorAll("iframe").forEach((iframe=>{const src=iframe.getAttribute("src");if(isMediaCMSUrl(src)){const wrapper=iframe.closest(".tiny-mediacms-iframe-wrapper")||iframe.closest(".tiny-iframe-responsive"),urlText=document.createTextNode(src),p=document.createElement("p");p.appendChild(urlText),wrapper?(wrapper.parentNode.insertBefore(p,wrapper),wrapper.remove()):(iframe.parentNode.insertBefore(p,iframe),iframe.remove())}})),tempDiv.querySelectorAll(".tiny-mediacms-iframe-wrapper").forEach((wrapper=>{const iframe=wrapper.querySelector("iframe");iframe&&wrapper.parentNode.insertBefore(iframe,wrapper),wrapper.remove()})),tempDiv.querySelectorAll(".tiny-iframe-responsive").forEach((wrapper=>{const iframe=wrapper.querySelector("iframe");iframe&&wrapper.parentNode.insertBefore(iframe,wrapper),wrapper.remove()})),e.content=tempDiv.innerHTML}})),pluginMetadata))),resolve(["".concat(_common.component,"/plugin"),Configuration])}));return _exports.default=_default,_exports.default}));
|
||||
|
||||
//# sourceMappingURL=plugin.min.js.map
|
||||
1
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/plugin.min.js.map
Executable file
1
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/plugin.min.js.map
Executable file
File diff suppressed because one or more lines are too long
3
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/selectors.min.js
vendored
Executable file
3
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/selectors.min.js
vendored
Executable file
@@ -0,0 +1,3 @@
|
||||
define("tiny_mediacms/selectors",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;return _exports.default={IMAGE:{actions:{submit:".tiny_imagecms_urlentrysubmit",imageBrowser:".openimagecmsbrowser",addUrl:".tiny_imagecms_addurl",deleteImage:".tiny_imagecms_deleteicon"},elements:{form:"form.tiny_imagecms_form",alignSettings:".tiny_imagecms_button",alt:".tiny_imagecms_altentry",altWarning:".tiny_imagecms_altwarning",height:".tiny_imagecms_heightentry",width:".tiny_imagecms_widthentry",url:".tiny_imagecms_urlentry",urlWarning:".tiny_imagecms_urlwarning",size:".tiny_imagecms_size",presentation:".tiny_imagecms_presentation",constrain:".tiny_imagecms_constrain",customStyle:".tiny_imagecms_customstyle",preview:".tiny_imagecms_preview",previewBox:".tiny_imagecms_preview_box",loaderIcon:".tiny_imagecms_loader",loaderIconContainer:".tiny_imagecms_loader_container",insertImage:".tiny_imagecms_insert_image",modalFooter:".modal-footer",dropzoneContainer:".tiny_imagecms_dropzone_container",fileInput:"#tiny_imagecms_fileinput",fileNameLabel:".tiny_imagecms_filename",sizeOriginal:".tiny_imagecms_sizeoriginal",sizeCustom:".tiny_imagecms_sizecustom",properties:".tiny_imagecms_properties"},styles:{responsive:"img-fluid"}},EMBED:{actions:{submit:".tiny_mediacms_submit",mediaBrowser:".openmediacmsbrowser"},elements:{form:"form.tiny_mediacms_form",source:".tiny_mediacms_source",track:".tiny_mediacms_track",mediaSource:".tiny_mediacms_media_source",linkSource:".tiny_mediacms_link_source",linkSize:".tiny_mediacms_link_size",posterSource:".tiny_mediacms_poster_source",posterSize:".tiny_mediacms_poster_size",displayOptions:".tiny_mediacms_display_options",name:".tiny_mediacms_name_entry",title:".tiny_mediacms_title_entry",url:".tiny_mediacms_url_entry",width:".tiny_mediacms_width_entry",height:".tiny_mediacms_height_entry",trackSource:".tiny_mediacms_track_source",trackKind:".tiny_mediacms_track_kind_entry",trackLabel:".tiny_mediacms_track_label_entry",trackLang:".tiny_mediacms_track_lang_entry",trackDefault:".tiny_mediacms_track_default",mediaControl:".tiny_mediacms_controls",mediaAutoplay:".tiny_mediacms_autoplay",mediaMute:".tiny_mediacms_mute",mediaLoop:".tiny_mediacms_loop",advancedSettings:".tiny_mediacms_advancedsettings",linkTab:'li[data-medium-type="link"]',videoTab:'li[data-medium-type="video"]',audioTab:'li[data-medium-type="audio"]',linkPane:'.tab-pane[data-medium-type="link"]',videoPane:'.tab-pane[data-medium-type="video"]',audioPane:'.tab-pane[data-medium-type="audio"]',trackSubtitlesTab:'li[data-track-kind="subtitles"]',trackCaptionsTab:'li[data-track-kind="captions"]',trackDescriptionsTab:'li[data-track-kind="descriptions"]',trackChaptersTab:'li[data-track-kind="chapters"]',trackMetadataTab:'li[data-track-kind="metadata"]',trackSubtitlesPane:'.tab-pane[data-track-kind="subtitles"]',trackCaptionsPane:'.tab-pane[data-track-kind="captions"]',trackDescriptionsPane:'.tab-pane[data-track-kind="descriptions"]',trackChaptersPane:'.tab-pane[data-track-kind="chapters"]',trackMetadataPane:'.tab-pane[data-track-kind="metadata"]'},mediaTypes:{link:"LINK",video:"VIDEO",audio:"AUDIO"},trackKinds:{subtitles:"SUBTITLES",captions:"CAPTIONS",descriptions:"DESCRIPTIONS",chapters:"CHAPTERS",metadata:"METADATA"}},IFRAME:{actions:{remove:'[data-action="remove"]'},elements:{form:"form.tiny_iframecms_form",url:".tiny_iframecms_url",urlWarning:".tiny_iframecms_url_warning",showTitle:".tiny_iframecms_showtitle",linkTitle:".tiny_iframecms_linktitle",showRelated:".tiny_iframecms_showrelated",showUserAvatar:".tiny_iframecms_showuseravatar",responsive:".tiny_iframecms_responsive",startAt:".tiny_iframecms_startat",startAtEnabled:".tiny_iframecms_startat_enabled",aspectRatio:".tiny_iframecms_aspectratio",width:".tiny_iframecms_width",height:".tiny_iframecms_height",preview:".tiny_iframecms_preview",previewContainer:".tiny_iframecms_preview_container",tabs:".tiny_iframecms_tabs",tabUrlBtn:".tiny_iframecms_tab_url_btn",tabIframeLibraryBtn:".tiny_iframecms_tab_iframe_library_btn",paneUrl:".tiny_iframecms_pane_url",paneIframeLibrary:".tiny_iframecms_pane_iframe_library",iframeLibraryContainer:".tiny_iframecms_iframe_library_container",iframeLibraryPlaceholder:".tiny_iframecms_iframe_library_placeholder",iframeLibraryLoading:".tiny_iframecms_iframe_library_loading",iframeLibraryFrame:".tiny_iframecms_iframe_library_frame"},aspectRatios:{"16:9":{width:560,height:315},"4:3":{width:560,height:420},"1:1":{width:400,height:400},custom:null}}},_exports.default}));
|
||||
|
||||
//# sourceMappingURL=selectors.min.js.map
|
||||
1
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/selectors.min.js.map
Executable file
1
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/selectors.min.js.map
Executable file
File diff suppressed because one or more lines are too long
10
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/usedfiles.min.js
vendored
Executable file
10
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/usedfiles.min.js
vendored
Executable file
@@ -0,0 +1,10 @@
|
||||
define("tiny_mediacms/usedfiles",["exports","core/templates","core/config"],(function(_exports,Templates,_config){var obj;function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,Templates=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}
|
||||
/**
|
||||
* Tiny Media Manager usedfiles.
|
||||
*
|
||||
* @module tiny_mediacms/usedfiles
|
||||
* @copyright 2022, Stevani Andolo <stevani@hotmail.com.au>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/(Templates),_config=(obj=_config)&&obj.__esModule?obj:{default:obj};class UsedFileManager{constructor(files,userContext,itemId,elementId){this.files=files,this.userContext=userContext,this.itemId=itemId,this.elementId=elementId}getElementId(){return this.elementId}getUsedFiles(){const editor=window.parent.tinymce.EditorManager.get(this.getElementId());if(!editor)return window.console.error("Editor not found for ".concat(this.getElementId())),[];const content=editor.getContent(),baseUrl="".concat(_config.default.wwwroot,"/draftfile.php/").concat(this.userContext,"/user/draft/").concat(this.itemId,"/"),pattern=new RegExp("[\"']"+baseUrl.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&")+"(?<filename>.+?)[\\?\"']","gm");return[...content.matchAll(pattern)].map((match=>decodeURIComponent(match.groups.filename)))}findUnusedFiles(usedFiles){return Object.entries(this.files).filter((_ref=>{let[filename]=_ref;return!usedFiles.includes(filename)})).map((_ref2=>{let[filename]=_ref2;return filename}))}findMissingFiles(usedFiles){return usedFiles.filter((filename=>!this.files.hasOwnProperty(filename)))}updateFiles(){const form=document.querySelector("form"),usedFiles=this.getUsedFiles(),unusedFiles=this.findUnusedFiles(usedFiles),missingFiles=this.findMissingFiles(usedFiles);return form.querySelectorAll('input[type=checkbox][name^="deletefile"]').forEach((checkbox=>{unusedFiles.includes(checkbox.dataset.filename)||checkbox.closest(".fitem").remove()})),form.classList.toggle("has-missing-files",!!missingFiles.length),form.classList.toggle("has-unused-files",!!unusedFiles.length),Templates.renderForPromise("tiny_mediacms/missingfiles",{missingFiles:missingFiles}).then((_ref3=>{let{html:html,js:js}=_ref3;Templates.replaceNodeContents(form.querySelector(".missing-files"),html,js)}))}}_exports.init=(files,usercontext,itemid,elementid)=>{const manager=new UsedFileManager(files,usercontext,itemid,elementid);return manager.updateFiles(),manager}}));
|
||||
|
||||
//# sourceMappingURL=usedfiles.min.js.map
|
||||
1
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/usedfiles.min.js.map
Executable file
1
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/usedfiles.min.js.map
Executable file
File diff suppressed because one or more lines are too long
264
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/autoconvert.js
Executable file
264
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/autoconvert.js
Executable file
@@ -0,0 +1,264 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Tiny MediaCMS Auto-convert module.
|
||||
*
|
||||
* This module automatically converts pasted MediaCMS URLs into embedded videos.
|
||||
* When a user pastes a MediaCMS video URL (e.g., https://deic.mediacms.io/view?m=JpBd1Zvdl),
|
||||
* it will be automatically converted to an iframe embed.
|
||||
*
|
||||
* @module tiny_mediacms/autoconvert
|
||||
* @copyright 2024
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
import {getData} from './options';
|
||||
|
||||
/**
|
||||
* Regular expression patterns for MediaCMS URLs.
|
||||
* Matches URLs like:
|
||||
* - https://deic.mediacms.io/view?m=JpBd1Zvdl
|
||||
* - https://example.mediacms.io/view?m=VIDEO_ID
|
||||
* - Custom domains configured in the plugin
|
||||
*/
|
||||
const MEDIACMS_VIEW_URL_PATTERN = /^(https?:\/\/[^\/]+)\/view\?m=([a-zA-Z0-9_-]+)$/;
|
||||
|
||||
/**
|
||||
* Check if a string is a valid MediaCMS view URL.
|
||||
*
|
||||
* @param {string} text - The text to check
|
||||
* @returns {Object|null} - Parsed URL info or null if not a valid MediaCMS URL
|
||||
*/
|
||||
const parseMediaCMSUrl = (text) => {
|
||||
if (!text || typeof text !== 'string') {
|
||||
return null;
|
||||
}
|
||||
|
||||
const trimmed = text.trim();
|
||||
|
||||
// Check for MediaCMS view URL pattern
|
||||
const match = trimmed.match(MEDIACMS_VIEW_URL_PATTERN);
|
||||
if (match) {
|
||||
return {
|
||||
baseUrl: match[1],
|
||||
videoId: match[2],
|
||||
originalUrl: trimmed,
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the pasted URL's domain is allowed based on configuration.
|
||||
*
|
||||
* @param {Object} parsed - Parsed URL info
|
||||
* @param {Object} config - Plugin configuration
|
||||
* @returns {boolean} - True if the domain is allowed
|
||||
*/
|
||||
const isDomainAllowed = (parsed, config) => {
|
||||
// If no specific base URL is configured, allow all MediaCMS domains
|
||||
const configuredBaseUrl = config.autoConvertBaseUrl || config.mediacmsBaseUrl;
|
||||
if (!configuredBaseUrl) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if the URL's base matches the configured base URL
|
||||
try {
|
||||
const configuredUrl = new URL(configuredBaseUrl);
|
||||
const pastedUrl = new URL(parsed.baseUrl);
|
||||
return configuredUrl.host === pastedUrl.host;
|
||||
} catch (e) {
|
||||
// If URL parsing fails, allow the conversion
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate the iframe embed HTML for a MediaCMS video.
|
||||
*
|
||||
* @param {Object} parsed - Parsed URL info
|
||||
* @param {Object} options - Embed options
|
||||
* @returns {string} - The iframe HTML
|
||||
*/
|
||||
const generateEmbedHtml = (parsed, options = {}) => {
|
||||
// Build the embed URL with default options
|
||||
const embedUrl = new URL(`${parsed.baseUrl}/embed`);
|
||||
embedUrl.searchParams.set('m', parsed.videoId);
|
||||
|
||||
// Apply default options (all enabled by default for best user experience)
|
||||
embedUrl.searchParams.set('showTitle', options.showTitle !== false ? '1' : '0');
|
||||
embedUrl.searchParams.set('showRelated', options.showRelated !== false ? '1' : '0');
|
||||
embedUrl.searchParams.set('showUserAvatar', options.showUserAvatar !== false ? '1' : '0');
|
||||
embedUrl.searchParams.set('linkTitle', options.linkTitle !== false ? '1' : '0');
|
||||
|
||||
// Generate responsive iframe HTML matching the template output format.
|
||||
// Uses aspect-ratio CSS for responsive sizing (16:9 default).
|
||||
// The wrapper will be added by editor for UI (edit button), then stripped on save.
|
||||
const html = `<iframe src="${embedUrl.toString()}" ` +
|
||||
`style="width: 100%; aspect-ratio: 16 / 9; display: block; border: 0;" ` +
|
||||
`allowfullscreen="allowfullscreen"></iframe>`;
|
||||
|
||||
return html;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set up auto-conversion for the editor.
|
||||
* This registers event handlers to detect pasted MediaCMS URLs.
|
||||
*
|
||||
* @param {TinyMCE} editor - The TinyMCE editor instance
|
||||
*/
|
||||
export const setupAutoConvert = (editor) => {
|
||||
const config = getData(editor) || {};
|
||||
|
||||
// Check if auto-convert is enabled (default: true)
|
||||
if (config.autoConvertEnabled === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle paste events
|
||||
editor.on('paste', (e) => {
|
||||
handlePasteEvent(editor, e, config);
|
||||
});
|
||||
|
||||
// Also handle input events for drag-and-drop text or keyboard paste
|
||||
editor.on('input', (e) => {
|
||||
handleInputEvent(editor, e, config);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle paste events to detect and convert MediaCMS URLs.
|
||||
*
|
||||
* @param {TinyMCE} editor - The TinyMCE editor instance
|
||||
* @param {Event} e - The paste event
|
||||
* @param {Object} config - Plugin configuration
|
||||
*/
|
||||
const handlePasteEvent = (editor, e, config) => {
|
||||
// Get pasted text from clipboard
|
||||
const clipboardData = e.clipboardData || window.clipboardData;
|
||||
if (!clipboardData) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to get plain text first
|
||||
const text = clipboardData.getData('text/plain') || clipboardData.getData('text');
|
||||
if (!text) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if it's a MediaCMS URL
|
||||
const parsed = parseMediaCMSUrl(text);
|
||||
if (!parsed) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if domain is allowed
|
||||
if (!isDomainAllowed(parsed, config)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevent default paste behavior
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
// Generate and insert the embed HTML
|
||||
const embedHtml = generateEmbedHtml(parsed, config.autoConvertOptions || {});
|
||||
|
||||
// Use a slight delay to ensure the editor is ready
|
||||
setTimeout(() => {
|
||||
editor.insertContent(embedHtml);
|
||||
// Move cursor after the inserted content
|
||||
editor.selection.collapse(false);
|
||||
}, 0);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle input events to catch URLs that might have been pasted without triggering paste event.
|
||||
* This is a fallback for certain browsers/scenarios.
|
||||
*
|
||||
* @param {TinyMCE} editor - The TinyMCE editor instance
|
||||
* @param {Event} e - The input event
|
||||
* @param {Object} config - Plugin configuration
|
||||
*/
|
||||
const handleInputEvent = (editor, e, config) => {
|
||||
// Only process inputType 'insertFromPaste' if paste event didn't catch it
|
||||
if (e.inputType !== 'insertFromPaste' && e.inputType !== 'insertText') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the current node and check if it contains just a URL
|
||||
const node = editor.selection.getNode();
|
||||
if (!node || node.nodeName !== 'P') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the paragraph contains only a MediaCMS URL
|
||||
const text = node.textContent || '';
|
||||
const parsed = parseMediaCMSUrl(text);
|
||||
|
||||
if (!parsed || !isDomainAllowed(parsed, config)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't convert if there's other content in the paragraph
|
||||
const trimmedHtml = node.innerHTML.trim();
|
||||
if (trimmedHtml !== text.trim() && !trimmedHtml.startsWith(text.trim())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate the embed HTML
|
||||
const embedHtml = generateEmbedHtml(parsed, config.autoConvertOptions || {});
|
||||
|
||||
// Replace the paragraph content with the embed
|
||||
// Use a slight delay to let the input event complete
|
||||
setTimeout(() => {
|
||||
// Re-check that the node still contains the URL (user might have typed more)
|
||||
const currentText = node.textContent || '';
|
||||
const currentParsed = parseMediaCMSUrl(currentText);
|
||||
|
||||
if (currentParsed && currentParsed.originalUrl === parsed.originalUrl) {
|
||||
// Select and replace the entire node
|
||||
editor.selection.select(node);
|
||||
editor.insertContent(embedHtml);
|
||||
}
|
||||
}, 100);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if a text is a MediaCMS URL (public helper).
|
||||
*
|
||||
* @param {string} text - The text to check
|
||||
* @returns {boolean} - True if it's a MediaCMS URL
|
||||
*/
|
||||
export const isMediaCMSUrl = (text) => {
|
||||
return parseMediaCMSUrl(text) !== null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert a MediaCMS URL to embed HTML (public helper).
|
||||
*
|
||||
* @param {string} url - The MediaCMS URL
|
||||
* @param {Object} options - Embed options
|
||||
* @returns {string|null} - The embed HTML or null if not a valid URL
|
||||
*/
|
||||
export const convertToEmbed = (url, options = {}) => {
|
||||
const parsed = parseMediaCMSUrl(url);
|
||||
if (!parsed) {
|
||||
return null;
|
||||
}
|
||||
return generateEmbedHtml(parsed, options);
|
||||
};
|
||||
282
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/commands.js
Executable file
282
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/commands.js
Executable file
@@ -0,0 +1,282 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Tiny Media commands.
|
||||
*
|
||||
* @module tiny_mediacms/commands
|
||||
* @copyright 2022 Huong Nguyen <huongnv13@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
import {getStrings} from 'core/str';
|
||||
import {
|
||||
component,
|
||||
iframeButtonName,
|
||||
iframeMenuItemName,
|
||||
iframeIcon,
|
||||
} from './common';
|
||||
import IframeEmbed from './iframeembed';
|
||||
import {getButtonImage} from 'editor_tiny/utils';
|
||||
|
||||
const isIframe = (node) => node.nodeName.toLowerCase() === 'iframe' ||
|
||||
(node.classList && node.classList.contains('tiny-iframe-responsive')) ||
|
||||
(node.classList && node.classList.contains('tiny-mediacms-iframe-wrapper'));
|
||||
|
||||
/**
|
||||
* Wrap iframes with overlay containers that allow hover detection.
|
||||
* Since iframes capture mouse events, we add an invisible overlay on top
|
||||
* that shows the edit button on hover.
|
||||
*
|
||||
* @param {TinyMCE} editor - The editor instance
|
||||
* @param {Function} handleIframeAction - The action to perform when clicking the button
|
||||
*/
|
||||
const setupIframeOverlays = (editor, handleIframeAction) => {
|
||||
/**
|
||||
* Process all iframes in the editor and add overlay wrappers.
|
||||
*/
|
||||
const processIframes = () => {
|
||||
const editorBody = editor.getBody();
|
||||
if (!editorBody) {
|
||||
return;
|
||||
}
|
||||
|
||||
const iframes = editorBody.querySelectorAll('iframe');
|
||||
iframes.forEach((iframe) => {
|
||||
// Skip if already wrapped
|
||||
if (iframe.parentElement?.classList.contains('tiny-mediacms-iframe-wrapper')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip TinyMCE internal iframes
|
||||
if (iframe.hasAttribute('data-mce-object') || iframe.hasAttribute('data-mce-placeholder')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create wrapper div
|
||||
const wrapper = editor.getDoc().createElement('div');
|
||||
wrapper.className = 'tiny-mediacms-iframe-wrapper';
|
||||
wrapper.setAttribute('contenteditable', 'false');
|
||||
|
||||
// Create edit button (positioned inside wrapper, over the iframe)
|
||||
const editBtn = editor.getDoc().createElement('button');
|
||||
editBtn.className = 'tiny-mediacms-edit-btn';
|
||||
editBtn.setAttribute('type', 'button');
|
||||
editBtn.setAttribute('title', 'Edit video embed options');
|
||||
// Use clean inline SVG to avoid TinyMCE wrapper issues
|
||||
editBtn.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">' +
|
||||
'<circle cx="50" cy="50" r="48" fill="#2EAF5A"/>' +
|
||||
'<polygon points="38,28 38,72 75,50" fill="#FFFFFF"/>' +
|
||||
'</svg>';
|
||||
|
||||
// Wrap the iframe: insert wrapper, move iframe into it, add button
|
||||
iframe.parentNode.insertBefore(wrapper, iframe);
|
||||
wrapper.appendChild(iframe);
|
||||
wrapper.appendChild(editBtn);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Add CSS styles for hover effects to the editor's document.
|
||||
*/
|
||||
const addStyles = () => {
|
||||
const editorDoc = editor.getDoc();
|
||||
if (!editorDoc) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if styles already added
|
||||
if (editorDoc.getElementById('tiny-mediacms-overlay-styles')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const style = editorDoc.createElement('style');
|
||||
style.id = 'tiny-mediacms-overlay-styles';
|
||||
style.textContent = `
|
||||
.tiny-mediacms-iframe-wrapper {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
line-height: 0;
|
||||
vertical-align: top;
|
||||
}
|
||||
.tiny-mediacms-iframe-wrapper iframe {
|
||||
display: block;
|
||||
}
|
||||
.tiny-mediacms-edit-btn {
|
||||
position: absolute;
|
||||
top: 48px;
|
||||
left: 6px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
background: #ffffff;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
z-index: 10;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
box-shadow: 0 2px 6px rgba(0,0,0,0.35);
|
||||
transition: transform 0.15s, box-shadow 0.15s;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.tiny-mediacms-edit-btn:hover {
|
||||
transform: scale(1.15);
|
||||
box-shadow: 0 3px 10px rgba(0,0,0,0.45);
|
||||
}
|
||||
.tiny-mediacms-edit-btn svg {
|
||||
width: 18px !important;
|
||||
height: 18px !important;
|
||||
display: block !important;
|
||||
}
|
||||
`;
|
||||
editorDoc.head.appendChild(style);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle click on the edit button.
|
||||
*
|
||||
* @param {Event} e - The click event
|
||||
*/
|
||||
const handleOverlayClick = (e) => {
|
||||
const target = e.target;
|
||||
|
||||
// Check if clicked on edit button or its child (svg/path)
|
||||
const editBtn = target.closest('.tiny-mediacms-edit-btn');
|
||||
if (!editBtn) {
|
||||
return;
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
// Find the associated wrapper and iframe
|
||||
const wrapper = editBtn.closest('.tiny-mediacms-iframe-wrapper');
|
||||
if (!wrapper) {
|
||||
return;
|
||||
}
|
||||
|
||||
const iframe = wrapper.querySelector('iframe');
|
||||
if (!iframe) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Select the wrapper so TinyMCE knows which element is selected
|
||||
editor.selection.select(wrapper);
|
||||
|
||||
// Open the edit dialog
|
||||
handleIframeAction();
|
||||
};
|
||||
|
||||
// Setup on editor init
|
||||
editor.on('init', () => {
|
||||
addStyles();
|
||||
processIframes();
|
||||
|
||||
// Handle clicks on the overlay
|
||||
editor.getBody().addEventListener('click', handleOverlayClick);
|
||||
});
|
||||
|
||||
// Re-process when content changes
|
||||
editor.on('SetContent', () => {
|
||||
processIframes();
|
||||
});
|
||||
|
||||
// Re-process when content is pasted
|
||||
editor.on('PastePostProcess', () => {
|
||||
setTimeout(processIframes, 100);
|
||||
});
|
||||
|
||||
// Re-process after undo/redo
|
||||
editor.on('Undo Redo', () => {
|
||||
processIframes();
|
||||
});
|
||||
|
||||
// Re-process on any content change (covers modal updates)
|
||||
editor.on('Change', () => {
|
||||
setTimeout(processIframes, 50);
|
||||
});
|
||||
|
||||
// Re-process when node changes (selection changes)
|
||||
editor.on('NodeChange', () => {
|
||||
processIframes();
|
||||
});
|
||||
};
|
||||
|
||||
const registerIframeCommand = (editor, iframeButtonText, iframeButtonImage) => {
|
||||
const handleIframeAction = () => {
|
||||
const iframeEmbed = new IframeEmbed(editor);
|
||||
iframeEmbed.displayDialogue();
|
||||
};
|
||||
|
||||
// Register the iframe icon
|
||||
editor.ui.registry.addIcon(iframeIcon, iframeButtonImage.html);
|
||||
|
||||
// Register the Menu Button as a toggle.
|
||||
// This means that when highlighted over an existing iframe element it will show as toggled on.
|
||||
editor.ui.registry.addToggleButton(iframeButtonName, {
|
||||
icon: iframeIcon,
|
||||
tooltip: iframeButtonText,
|
||||
onAction: handleIframeAction,
|
||||
onSetup: api => {
|
||||
return editor.selection.selectorChangedWithUnbind(
|
||||
'iframe:not([data-mce-object]):not([data-mce-placeholder]),.tiny-iframe-responsive,.tiny-mediacms-iframe-wrapper',
|
||||
api.setActive
|
||||
).unbind;
|
||||
}
|
||||
});
|
||||
|
||||
editor.ui.registry.addMenuItem(iframeMenuItemName, {
|
||||
icon: iframeIcon,
|
||||
text: iframeButtonText,
|
||||
onAction: handleIframeAction,
|
||||
});
|
||||
|
||||
editor.ui.registry.addContextToolbar(iframeButtonName, {
|
||||
predicate: isIframe,
|
||||
items: iframeButtonName,
|
||||
position: 'node',
|
||||
scope: 'node'
|
||||
});
|
||||
|
||||
editor.ui.registry.addContextMenu(iframeButtonName, {
|
||||
update: isIframe,
|
||||
});
|
||||
|
||||
// Setup iframe overlays with edit button on hover
|
||||
setupIframeOverlays(editor, handleIframeAction);
|
||||
};
|
||||
|
||||
export const getSetup = async() => {
|
||||
const [
|
||||
iframeButtonText,
|
||||
] = await getStrings([
|
||||
'iframebuttontitle',
|
||||
].map((key) => ({key, component})));
|
||||
|
||||
const [
|
||||
iframeButtonImage,
|
||||
] = await Promise.all([
|
||||
getButtonImage('icon', component),
|
||||
]);
|
||||
|
||||
// Note: The function returned here must be synchronous and cannot use promises.
|
||||
// All promises must be resolved prior to returning the function.
|
||||
return (editor) => {
|
||||
registerIframeCommand(editor, iframeButtonText, iframeButtonImage);
|
||||
};
|
||||
};
|
||||
30
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/common.js
Executable file
30
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/common.js
Executable file
@@ -0,0 +1,30 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Tiny Media common values.
|
||||
*
|
||||
* @module tiny_mediacms/common
|
||||
* @copyright 2022 Huong Nguyen <huongnv13@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
export default {
|
||||
pluginName: 'tiny_mediacms/plugin',
|
||||
component: 'tiny_mediacms',
|
||||
iframeButtonName: 'tiny_mediacms_iframe',
|
||||
iframeMenuItemName: 'tiny_mediacms_iframe',
|
||||
iframeIcon: 'tiny_mediacms_iframe',
|
||||
};
|
||||
60
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/configuration.js
Executable file
60
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/configuration.js
Executable file
@@ -0,0 +1,60 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Tiny Media configuration.
|
||||
*
|
||||
* @module tiny_mediacms/configuration
|
||||
* @copyright 2022 Huong Nguyen <huongnv13@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
import {
|
||||
iframeButtonName,
|
||||
iframeMenuItemName,
|
||||
} from './common';
|
||||
import {
|
||||
addContextmenuItem,
|
||||
} from 'editor_tiny/utils';
|
||||
|
||||
const configureMenu = (menu) => {
|
||||
// Add the Iframe Embed to the insert menu.
|
||||
menu.insert.items = `${iframeMenuItemName} ${menu.insert.items}`;
|
||||
|
||||
return menu;
|
||||
};
|
||||
|
||||
const configureToolbar = (toolbar) => {
|
||||
// The toolbar contains an array of named sections.
|
||||
// The Moodle integration ensures that there is a section called 'content'.
|
||||
|
||||
return toolbar.map((section) => {
|
||||
if (section.name === 'content') {
|
||||
// Insert the iframe button at the start of it.
|
||||
section.items.unshift(iframeButtonName);
|
||||
}
|
||||
|
||||
return section;
|
||||
});
|
||||
};
|
||||
|
||||
export const configure = (instanceConfig) => {
|
||||
// Update the instance configuration to add the Iframe Embed menu option to the menus and toolbars.
|
||||
return {
|
||||
contextmenu: addContextmenuItem(instanceConfig.contextmenu, iframeButtonName),
|
||||
menu: configureMenu(instanceConfig.menu),
|
||||
toolbar: configureToolbar(instanceConfig.toolbar),
|
||||
};
|
||||
};
|
||||
467
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/embed.js
Executable file
467
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/embed.js
Executable file
@@ -0,0 +1,467 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Tiny Media plugin Embed class for Moodle.
|
||||
*
|
||||
* @module tiny_mediacms/embed
|
||||
* @copyright 2022 Huong Nguyen <huongnv13@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
import Templates from 'core/templates';
|
||||
import {
|
||||
getString,
|
||||
getStrings,
|
||||
} from 'core/str';
|
||||
import * as ModalEvents from 'core/modal_events';
|
||||
import {displayFilepicker} from 'editor_tiny/utils';
|
||||
import {getCurrentLanguage, getMoodleLang} from 'editor_tiny/options';
|
||||
import {component} from "./common";
|
||||
import EmbedModal from './embedmodal';
|
||||
import Selectors from './selectors';
|
||||
import {getEmbedPermissions} from './options';
|
||||
import {getFilePicker} from 'editor_tiny/options';
|
||||
|
||||
export default class MediaEmbed {
|
||||
editor = null;
|
||||
canShowFilePicker = false;
|
||||
canShowFilePickerPoster = false;
|
||||
canShowFilePickerTrack = false;
|
||||
|
||||
/**
|
||||
* @property {Object} The names of the alignment options.
|
||||
*/
|
||||
helpStrings = null;
|
||||
|
||||
/**
|
||||
* @property {boolean} Indicate that the user is updating the media or not.
|
||||
*/
|
||||
isUpdating = false;
|
||||
|
||||
/**
|
||||
* @property {Object} The currently selected media.
|
||||
*/
|
||||
selectedMedia = null;
|
||||
|
||||
constructor(editor) {
|
||||
const permissions = getEmbedPermissions(editor);
|
||||
|
||||
// Indicates whether the file picker can be shown.
|
||||
this.canShowFilePicker = permissions.filepicker && (typeof getFilePicker(editor, 'media') !== 'undefined');
|
||||
this.canShowFilePickerPoster = permissions.filepicker && (typeof getFilePicker(editor, 'image') !== 'undefined');
|
||||
this.canShowFilePickerTrack = permissions.filepicker && (typeof getFilePicker(editor, 'subtitle') !== 'undefined');
|
||||
|
||||
this.editor = editor;
|
||||
}
|
||||
|
||||
async getHelpStrings() {
|
||||
if (!this.helpStrings) {
|
||||
const [addSource, tracks, subtitles, captions, descriptions, chapters, metadata] = await getStrings([
|
||||
'addsource_help',
|
||||
'tracks_help',
|
||||
'subtitles_help',
|
||||
'captions_help',
|
||||
'descriptions_help',
|
||||
'chapters_help',
|
||||
'metadata_help',
|
||||
].map((key) => ({
|
||||
key,
|
||||
component,
|
||||
})));
|
||||
|
||||
this.helpStrings = {addSource, tracks, subtitles, captions, descriptions, chapters, metadata};
|
||||
}
|
||||
|
||||
return this.helpStrings;
|
||||
}
|
||||
|
||||
async getTemplateContext(data) {
|
||||
const languages = this.prepareMoodleLang();
|
||||
|
||||
const helpIcons = Array.from(Object.entries(await this.getHelpStrings())).forEach(([key, text]) => {
|
||||
data[`${key.toLowerCase()}helpicon`] = {text};
|
||||
});
|
||||
|
||||
return Object.assign({}, {
|
||||
elementid: this.editor.getElement().id,
|
||||
showfilepicker: this.canShowFilePicker,
|
||||
showfilepickerposter: this.canShowFilePickerPoster,
|
||||
showfilepickertrack: this.canShowFilePickerTrack,
|
||||
langsinstalled: languages.installed,
|
||||
langsavailable: languages.available,
|
||||
link: true,
|
||||
video: false,
|
||||
audio: false,
|
||||
isupdating: this.isUpdating,
|
||||
}, data, helpIcons);
|
||||
}
|
||||
|
||||
async displayDialogue() {
|
||||
this.selectedMedia = this.getSelectedMedia();
|
||||
const data = Object.assign({}, this.getCurrentEmbedData());
|
||||
this.isUpdating = Object.keys(data).length !== 0;
|
||||
|
||||
this.currentModal = await EmbedModal.create({
|
||||
title: getString('createmedia', 'tiny_mediacms'),
|
||||
templateContext: await this.getTemplateContext(data),
|
||||
});
|
||||
|
||||
await this.registerEventListeners(this.currentModal);
|
||||
}
|
||||
|
||||
getCurrentEmbedData() {
|
||||
const properties = this.getMediumProperties();
|
||||
if (!properties) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const processedProperties = {};
|
||||
processedProperties[properties.type.toLowerCase()] = properties;
|
||||
processedProperties.link = false;
|
||||
|
||||
return processedProperties;
|
||||
}
|
||||
|
||||
getSelectedMedia() {
|
||||
const mediaElm = this.editor.selection.getNode();
|
||||
|
||||
if (!mediaElm) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (mediaElm.nodeName.toLowerCase() === 'video' || mediaElm.nodeName.toLowerCase() === 'audio') {
|
||||
return mediaElm;
|
||||
}
|
||||
|
||||
if (mediaElm.querySelector('video')) {
|
||||
return mediaElm.querySelector('video');
|
||||
}
|
||||
|
||||
if (mediaElm.querySelector('audio')) {
|
||||
return mediaElm.querySelector('audio');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
getMediumProperties() {
|
||||
const boolAttr = (elem, attr) => {
|
||||
// As explained in MDL-64175, some OS (like Ubuntu), are removing the value for these attributes.
|
||||
// So in order to check if attr="true", we need to check if the attribute exists and if the value is empty or true.
|
||||
return (elem.hasAttribute(attr) && (elem.getAttribute(attr) || elem.getAttribute(attr) === ''));
|
||||
};
|
||||
|
||||
const tracks = {
|
||||
subtitles: [],
|
||||
captions: [],
|
||||
descriptions: [],
|
||||
chapters: [],
|
||||
metadata: []
|
||||
};
|
||||
const sources = [];
|
||||
|
||||
const medium = this.selectedMedia;
|
||||
if (!medium) {
|
||||
return null;
|
||||
}
|
||||
medium.querySelectorAll('track').forEach((track) => {
|
||||
tracks[track.getAttribute('kind')].push({
|
||||
src: track.getAttribute('src'),
|
||||
srclang: track.getAttribute('srclang'),
|
||||
label: track.getAttribute('label'),
|
||||
defaultTrack: boolAttr(track, 'default')
|
||||
});
|
||||
});
|
||||
|
||||
medium.querySelectorAll('source').forEach((source) => {
|
||||
sources.push(source.src);
|
||||
});
|
||||
|
||||
return {
|
||||
type: medium.nodeName.toLowerCase() === 'video' ? Selectors.EMBED.mediaTypes.video : Selectors.EMBED.mediaTypes.audio,
|
||||
sources,
|
||||
poster: medium.getAttribute('poster'),
|
||||
title: medium.getAttribute('title'),
|
||||
width: medium.getAttribute('width'),
|
||||
height: medium.getAttribute('height'),
|
||||
autoplay: boolAttr(medium, 'autoplay'),
|
||||
loop: boolAttr(medium, 'loop'),
|
||||
muted: boolAttr(medium, 'muted'),
|
||||
controls: boolAttr(medium, 'controls'),
|
||||
tracks,
|
||||
};
|
||||
}
|
||||
|
||||
prepareMoodleLang() {
|
||||
const moodleLangs = getMoodleLang(this.editor);
|
||||
const currentLanguage = getCurrentLanguage(this.editor);
|
||||
|
||||
const installed = Object.entries(moodleLangs.installed).map(([lang, code]) => ({
|
||||
lang,
|
||||
code,
|
||||
"default": lang === currentLanguage,
|
||||
}));
|
||||
|
||||
const available = Object.entries(moodleLangs.available).map(([lang, code]) => ({
|
||||
lang,
|
||||
code,
|
||||
"default": lang === currentLanguage,
|
||||
}));
|
||||
|
||||
return {
|
||||
installed,
|
||||
available,
|
||||
};
|
||||
}
|
||||
|
||||
getMoodleLangObj(subtitleLang) {
|
||||
const {available} = getMoodleLang(this.editor);
|
||||
|
||||
if (available[subtitleLang]) {
|
||||
return {
|
||||
lang: subtitleLang,
|
||||
code: available[subtitleLang],
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
filePickerCallback(params, element, fpType) {
|
||||
if (params.url !== '') {
|
||||
const tabPane = element.closest('.tab-pane');
|
||||
element.closest(Selectors.EMBED.elements.source).querySelector(Selectors.EMBED.elements.url).value = params.url;
|
||||
|
||||
if (tabPane.id === this.editor.getElement().id + '_' + Selectors.EMBED.mediaTypes.link.toLowerCase()) {
|
||||
tabPane.querySelector(Selectors.EMBED.elements.name).value = params.file;
|
||||
}
|
||||
|
||||
if (fpType === 'subtitle') {
|
||||
// If the file is subtitle file. We need to match the language and label for that file.
|
||||
const subtitleLang = params.file.split('.vtt')[0].split('-').slice(-1)[0];
|
||||
const langObj = this.getMoodleLangObj(subtitleLang);
|
||||
if (langObj) {
|
||||
const track = element.closest(Selectors.EMBED.elements.track);
|
||||
track.querySelector(Selectors.EMBED.elements.trackLabel).value = langObj.lang.trim();
|
||||
track.querySelector(Selectors.EMBED.elements.trackLang).value = langObj.code;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addMediaSourceComponent(element, callback) {
|
||||
const sourceElement = element.closest(Selectors.EMBED.elements.source + Selectors.EMBED.elements.mediaSource);
|
||||
const clone = sourceElement.cloneNode(true);
|
||||
|
||||
sourceElement.querySelector('.removecomponent-wrapper').classList.remove('hidden');
|
||||
sourceElement.querySelector('.addcomponent-wrapper').classList.add('hidden');
|
||||
|
||||
sourceElement.parentNode.insertBefore(clone, sourceElement.nextSibling);
|
||||
|
||||
if (callback) {
|
||||
callback(clone);
|
||||
}
|
||||
}
|
||||
|
||||
removeMediaSourceComponent(element) {
|
||||
const sourceElement = element.closest(Selectors.EMBED.elements.source + Selectors.EMBED.elements.mediaSource);
|
||||
sourceElement.remove();
|
||||
}
|
||||
|
||||
addTrackComponent(element, callback) {
|
||||
const trackElement = element.closest(Selectors.EMBED.elements.track);
|
||||
const clone = trackElement.cloneNode(true);
|
||||
|
||||
trackElement.querySelector('.removecomponent-wrapper').classList.remove('hidden');
|
||||
trackElement.querySelector('.addcomponent-wrapper').classList.add('hidden');
|
||||
|
||||
trackElement.parentNode.insertBefore(clone, trackElement.nextSibling);
|
||||
|
||||
if (callback) {
|
||||
callback(clone);
|
||||
}
|
||||
}
|
||||
|
||||
removeTrackComponent(element) {
|
||||
const sourceElement = element.closest(Selectors.EMBED.elements.track);
|
||||
sourceElement.remove();
|
||||
}
|
||||
|
||||
getMediumTypeFromTabPane(tabPane) {
|
||||
return tabPane.getAttribute('data-medium-type');
|
||||
}
|
||||
|
||||
getTrackTypeFromTabPane(tabPane) {
|
||||
return tabPane.getAttribute('data-track-kind');
|
||||
}
|
||||
|
||||
getMediaHTML(form) {
|
||||
const mediumType = this.getMediumTypeFromTabPane(form.querySelector('.root.tab-content > .tab-pane.active'));
|
||||
const tabContent = form.querySelector(Selectors.EMBED.elements[mediumType.toLowerCase() + 'Pane']);
|
||||
|
||||
return this['getMediaHTML' + mediumType[0].toUpperCase() + mediumType.substr(1)](tabContent);
|
||||
}
|
||||
|
||||
getMediaHTMLLink(tab) {
|
||||
const context = {
|
||||
url: tab.querySelector(Selectors.EMBED.elements.url).value,
|
||||
name: tab.querySelector(Selectors.EMBED.elements.name).value || false
|
||||
};
|
||||
|
||||
return context.url ? Templates.renderForPromise('tiny_mediacms/embed_media_link', context) : '';
|
||||
}
|
||||
|
||||
getMediaHTMLVideo(tab) {
|
||||
const context = this.getContextForMediaHTML(tab);
|
||||
context.width = tab.querySelector(Selectors.EMBED.elements.width).value || false;
|
||||
context.height = tab.querySelector(Selectors.EMBED.elements.height).value || false;
|
||||
context.poster = tab.querySelector(
|
||||
`${Selectors.EMBED.elements.posterSource} ${Selectors.EMBED.elements.url}`
|
||||
).value || false;
|
||||
|
||||
return context.sources.length ? Templates.renderForPromise('tiny_mediacms/embed_media_video', context) : '';
|
||||
}
|
||||
|
||||
getMediaHTMLAudio(tab) {
|
||||
const context = this.getContextForMediaHTML(tab);
|
||||
|
||||
return context.sources.length ? Templates.renderForPromise('tiny_mediacms/embed_media_audio', context) : '';
|
||||
}
|
||||
|
||||
getContextForMediaHTML(tab) {
|
||||
const tracks = Array.from(tab.querySelectorAll(Selectors.EMBED.elements.track)).map(track => ({
|
||||
track: track.querySelector(Selectors.EMBED.elements.trackSource + ' ' + Selectors.EMBED.elements.url).value,
|
||||
kind: this.getTrackTypeFromTabPane(track.closest('.tab-pane')),
|
||||
label: track.querySelector(Selectors.EMBED.elements.trackLabel).value ||
|
||||
track.querySelector(Selectors.EMBED.elements.trackLang).value,
|
||||
srclang: track.querySelector(Selectors.EMBED.elements.trackLang).value,
|
||||
defaultTrack: track.querySelector(Selectors.EMBED.elements.trackDefault).checked ? "true" : null
|
||||
})).filter((track) => !!track.track);
|
||||
|
||||
const sources = Array.from(tab.querySelectorAll(Selectors.EMBED.elements.mediaSource + ' '
|
||||
+ Selectors.EMBED.elements.url))
|
||||
.filter((source) => !!source.value)
|
||||
.map((source) => source.value);
|
||||
|
||||
return {
|
||||
sources,
|
||||
description: tab.querySelector(Selectors.EMBED.elements.mediaSource + ' '
|
||||
+ Selectors.EMBED.elements.url).value || false,
|
||||
tracks,
|
||||
showControls: tab.querySelector(Selectors.EMBED.elements.mediaControl).checked,
|
||||
autoplay: tab.querySelector(Selectors.EMBED.elements.mediaAutoplay).checked,
|
||||
muted: tab.querySelector(Selectors.EMBED.elements.mediaMute).checked,
|
||||
loop: tab.querySelector(Selectors.EMBED.elements.mediaLoop).checked,
|
||||
title: tab.querySelector(Selectors.EMBED.elements.title).value || false
|
||||
};
|
||||
}
|
||||
|
||||
getFilepickerTypeFromElement(element) {
|
||||
if (element.closest(Selectors.EMBED.elements.posterSource)) {
|
||||
return 'image';
|
||||
}
|
||||
if (element.closest(Selectors.EMBED.elements.trackSource)) {
|
||||
return 'subtitle';
|
||||
}
|
||||
|
||||
return 'media';
|
||||
}
|
||||
|
||||
async clickHandler(e) {
|
||||
const element = e.target;
|
||||
|
||||
const mediaBrowser = element.closest(Selectors.EMBED.actions.mediaBrowser);
|
||||
if (mediaBrowser) {
|
||||
e.preventDefault();
|
||||
const fpType = this.getFilepickerTypeFromElement(element);
|
||||
const params = await displayFilepicker(this.editor, fpType);
|
||||
this.filePickerCallback(params, element, fpType);
|
||||
}
|
||||
|
||||
const addComponentSourceAction = element.closest(Selectors.EMBED.elements.mediaSource + ' .addcomponent');
|
||||
if (addComponentSourceAction) {
|
||||
e.preventDefault();
|
||||
this.addMediaSourceComponent(element);
|
||||
}
|
||||
|
||||
const removeComponentSourceAction = element.closest(Selectors.EMBED.elements.mediaSource + ' .removecomponent');
|
||||
if (removeComponentSourceAction) {
|
||||
e.preventDefault();
|
||||
this.removeMediaSourceComponent(element);
|
||||
}
|
||||
|
||||
const addComponentTrackAction = element.closest(Selectors.EMBED.elements.track + ' .addcomponent');
|
||||
if (addComponentTrackAction) {
|
||||
e.preventDefault();
|
||||
this.addTrackComponent(element);
|
||||
}
|
||||
|
||||
const removeComponentTrackAction = element.closest(Selectors.EMBED.elements.track + ' .removecomponent');
|
||||
if (removeComponentTrackAction) {
|
||||
e.preventDefault();
|
||||
this.removeTrackComponent(element);
|
||||
}
|
||||
|
||||
// Only allow one track per tab to be selected as "default".
|
||||
const trackDefaultAction = element.closest(Selectors.EMBED.elements.trackDefault);
|
||||
if (trackDefaultAction && trackDefaultAction.checked) {
|
||||
const getKind = (el) => this.getTrackTypeFromTabPane(el.parentElement.closest('.tab-pane'));
|
||||
|
||||
element.parentElement
|
||||
.closest('.root.tab-content')
|
||||
.querySelectorAll(Selectors.EMBED.elements.trackDefault)
|
||||
.forEach((select) => {
|
||||
if (select !== element && getKind(element) === getKind(select)) {
|
||||
select.checked = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async handleDialogueSubmission(event, modal) {
|
||||
const {html} = await this.getMediaHTML(modal.getRoot()[0]);
|
||||
if (html) {
|
||||
if (this.isUpdating) {
|
||||
this.selectedMedia.outerHTML = html;
|
||||
this.isUpdating = false;
|
||||
} else {
|
||||
this.editor.insertContent(html);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async registerEventListeners(modal) {
|
||||
await modal.getBody();
|
||||
const $root = modal.getRoot();
|
||||
const root = $root[0];
|
||||
if (this.canShowFilePicker || this.canShowFilePickerPoster || this.canShowFilePickerTrack) {
|
||||
root.addEventListener('click', this.clickHandler.bind(this));
|
||||
}
|
||||
|
||||
$root.on(ModalEvents.save, this.handleDialogueSubmission.bind(this));
|
||||
$root.on(ModalEvents.hidden, () => {
|
||||
this.currentModal.destroy();
|
||||
});
|
||||
$root.on(ModalEvents.shown, () => {
|
||||
root.querySelectorAll(Selectors.EMBED.elements.trackLang).forEach((dropdown) => {
|
||||
const defaultVal = dropdown.getAttribute('data-value');
|
||||
if (defaultVal) {
|
||||
dropdown.value = defaultVal;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
47
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/embedmodal.js
Executable file
47
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/embedmodal.js
Executable file
@@ -0,0 +1,47 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Embedded Media Management Modal for Tiny.
|
||||
*
|
||||
* @module tiny_mediacms/embedmodal
|
||||
* @copyright 2022 Andrew Lyons <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
import Modal from 'core/modal';
|
||||
import {component} from './common';
|
||||
|
||||
export default class EmbedModal extends Modal {
|
||||
static TYPE = `${component}/modal`;
|
||||
static TEMPLATE = `${component}/embed_media_modal`;
|
||||
|
||||
registerEventListeners() {
|
||||
// Call the parent registration.
|
||||
super.registerEventListeners();
|
||||
|
||||
// Register to close on save/cancel.
|
||||
this.registerCloseOnSave();
|
||||
this.registerCloseOnCancel();
|
||||
}
|
||||
|
||||
configure(modalConfig) {
|
||||
modalConfig.large = true;
|
||||
modalConfig.removeOnClose = true;
|
||||
modalConfig.show = true;
|
||||
|
||||
super.configure(modalConfig);
|
||||
}
|
||||
}
|
||||
1130
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/iframeembed.js
Executable file
1130
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/iframeembed.js
Executable file
File diff suppressed because it is too large
Load Diff
47
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/iframemodal.js
Executable file
47
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/iframemodal.js
Executable file
@@ -0,0 +1,47 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Iframe Embed Modal for Tiny Media2.
|
||||
*
|
||||
* @module tiny_mediacms/iframemodal
|
||||
* @copyright 2024
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
import Modal from 'core/modal';
|
||||
import {component} from './common';
|
||||
|
||||
export default class IframeModal extends Modal {
|
||||
static TYPE = `${component}/iframemodal`;
|
||||
static TEMPLATE = `${component}/iframe_embed_modal`;
|
||||
|
||||
registerEventListeners() {
|
||||
// Call the parent registration.
|
||||
super.registerEventListeners();
|
||||
|
||||
// Register to close on save/cancel.
|
||||
this.registerCloseOnSave();
|
||||
this.registerCloseOnCancel();
|
||||
}
|
||||
|
||||
configure(modalConfig) {
|
||||
modalConfig.large = true;
|
||||
modalConfig.removeOnClose = true;
|
||||
modalConfig.show = true;
|
||||
|
||||
super.configure(modalConfig);
|
||||
}
|
||||
}
|
||||
273
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/image.js
Executable file
273
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/image.js
Executable file
@@ -0,0 +1,273 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Tiny Media plugin Image class for Moodle.
|
||||
*
|
||||
* @module tiny_mediacms/image
|
||||
* @copyright 2022 Huong Nguyen <huongnv13@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
import Selectors from './selectors';
|
||||
import ImageModal from './imagemodal';
|
||||
import {getImagePermissions} from './options';
|
||||
import {getFilePicker} from 'editor_tiny/options';
|
||||
import {ImageInsert} from 'tiny_mediacms/imageinsert';
|
||||
import {ImageDetails} from 'tiny_mediacms/imagedetails';
|
||||
import {prefetchStrings} from 'core/prefetch';
|
||||
import {getString} from 'core/str';
|
||||
import {
|
||||
bodyImageInsert,
|
||||
footerImageInsert,
|
||||
bodyImageDetails,
|
||||
footerImageDetails,
|
||||
showElements,
|
||||
hideElements,
|
||||
isPercentageValue,
|
||||
} from 'tiny_mediacms/imagehelpers';
|
||||
|
||||
prefetchStrings('tiny_mediacms', [
|
||||
'imageurlrequired',
|
||||
'sizecustom_help',
|
||||
]);
|
||||
|
||||
export default class MediaImage {
|
||||
canShowFilePicker = false;
|
||||
editor = null;
|
||||
currentModal = null;
|
||||
/**
|
||||
* @type {HTMLElement|null} The root element.
|
||||
*/
|
||||
root = null;
|
||||
|
||||
constructor(editor) {
|
||||
const permissions = getImagePermissions(editor);
|
||||
const options = getFilePicker(editor, 'image');
|
||||
// Indicates whether the file picker can be shown.
|
||||
this.canShowFilePicker = permissions.filepicker
|
||||
&& (typeof options !== 'undefined')
|
||||
&& Object.keys(options.repositories).length > 0;
|
||||
// Indicates whether the drop zone area can be shown.
|
||||
this.canShowDropZone = (typeof options !== 'undefined') &&
|
||||
Object.values(options.repositories).some(repository => repository.type === 'upload');
|
||||
|
||||
this.editor = editor;
|
||||
}
|
||||
|
||||
async displayDialogue() {
|
||||
const currentImageData = await this.getCurrentImageData();
|
||||
this.currentModal = await ImageModal.create();
|
||||
this.root = this.currentModal.getRoot()[0];
|
||||
if (currentImageData && currentImageData.src) {
|
||||
this.loadPreviewImage(currentImageData.src);
|
||||
} else {
|
||||
this.loadInsertImage();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays an insert image view asynchronously.
|
||||
*
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
loadInsertImage = async function() {
|
||||
const templateContext = {
|
||||
elementid: this.editor.id,
|
||||
showfilepicker: this.canShowFilePicker,
|
||||
showdropzone: this.canShowDropZone,
|
||||
};
|
||||
|
||||
Promise.all([bodyImageInsert(templateContext, this.root), footerImageInsert(templateContext, this.root)])
|
||||
.then(() => {
|
||||
const imageinsert = new ImageInsert(
|
||||
this.root,
|
||||
this.editor,
|
||||
this.currentModal,
|
||||
this.canShowFilePicker,
|
||||
this.canShowDropZone,
|
||||
);
|
||||
imageinsert.init();
|
||||
return;
|
||||
})
|
||||
.catch(error => {
|
||||
window.console.log(error);
|
||||
});
|
||||
};
|
||||
|
||||
async getTemplateContext(data) {
|
||||
return {
|
||||
elementid: this.editor.id,
|
||||
showfilepicker: this.canShowFilePicker,
|
||||
...data,
|
||||
};
|
||||
}
|
||||
|
||||
async getCurrentImageData() {
|
||||
const selectedImageProperties = this.getSelectedImageProperties();
|
||||
if (!selectedImageProperties) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const properties = {...selectedImageProperties};
|
||||
|
||||
if (properties.src) {
|
||||
properties.haspreview = true;
|
||||
}
|
||||
|
||||
if (!properties.alt) {
|
||||
properties.presentation = true;
|
||||
}
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously loads and previews an image from the provided URL.
|
||||
*
|
||||
* @param {string} url - The URL of the image to load and preview.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
loadPreviewImage = async function(url) {
|
||||
this.startImageLoading();
|
||||
const image = new Image();
|
||||
image.src = url;
|
||||
image.addEventListener('error', async() => {
|
||||
const urlWarningLabelEle = this.root.querySelector(Selectors.IMAGE.elements.urlWarning);
|
||||
urlWarningLabelEle.innerHTML = await getString('imageurlrequired', 'tiny_mediacms');
|
||||
showElements(Selectors.IMAGE.elements.urlWarning, this.root);
|
||||
this.stopImageLoading();
|
||||
});
|
||||
|
||||
image.addEventListener('load', async() => {
|
||||
const currentImageData = await this.getCurrentImageData();
|
||||
let templateContext = await this.getTemplateContext(currentImageData);
|
||||
templateContext.sizecustomhelpicon = {text: await getString('sizecustom_help', 'tiny_mediacms')};
|
||||
|
||||
Promise.all([bodyImageDetails(templateContext, this.root), footerImageDetails(templateContext, this.root)])
|
||||
.then(() => {
|
||||
this.stopImageLoading();
|
||||
return;
|
||||
})
|
||||
.then(() => {
|
||||
const imagedetails = new ImageDetails(
|
||||
this.root,
|
||||
this.editor,
|
||||
this.currentModal,
|
||||
this.canShowFilePicker,
|
||||
this.canShowDropZone,
|
||||
url,
|
||||
image,
|
||||
);
|
||||
imagedetails.init();
|
||||
return;
|
||||
})
|
||||
.catch(error => {
|
||||
window.console.log(error);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
getSelectedImageProperties() {
|
||||
const image = this.getSelectedImage();
|
||||
if (!image) {
|
||||
this.selectedImage = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
const properties = {
|
||||
src: null,
|
||||
alt: null,
|
||||
width: null,
|
||||
height: null,
|
||||
presentation: false,
|
||||
customStyle: '', // Custom CSS styles applied to the image.
|
||||
};
|
||||
|
||||
const getImageHeight = (image) => {
|
||||
if (!isPercentageValue(String(image.height))) {
|
||||
return parseInt(image.height, 10);
|
||||
}
|
||||
|
||||
return image.height;
|
||||
};
|
||||
|
||||
const getImageWidth = (image) => {
|
||||
if (!isPercentageValue(String(image.width))) {
|
||||
return parseInt(image.width, 10);
|
||||
}
|
||||
|
||||
return image.width;
|
||||
};
|
||||
|
||||
// Get the current selection.
|
||||
this.selectedImage = image;
|
||||
|
||||
properties.customStyle = image.style.cssText;
|
||||
|
||||
const width = getImageWidth(image);
|
||||
if (width !== 0) {
|
||||
properties.width = width;
|
||||
}
|
||||
|
||||
const height = getImageHeight(image);
|
||||
if (height !== 0) {
|
||||
properties.height = height;
|
||||
}
|
||||
|
||||
properties.src = image.getAttribute('src');
|
||||
properties.alt = image.getAttribute('alt') || '';
|
||||
properties.presentation = (image.getAttribute('role') === 'presentation');
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
getSelectedImage() {
|
||||
const imgElm = this.editor.selection.getNode();
|
||||
const figureElm = this.editor.dom.getParent(imgElm, 'figure.image');
|
||||
if (figureElm) {
|
||||
return this.editor.dom.select('img', figureElm)[0];
|
||||
}
|
||||
|
||||
if (imgElm && (imgElm.nodeName.toUpperCase() !== 'IMG' || this.isPlaceholderImage(imgElm))) {
|
||||
return null;
|
||||
}
|
||||
return imgElm;
|
||||
}
|
||||
|
||||
isPlaceholderImage(imgElm) {
|
||||
if (imgElm.nodeName.toUpperCase() !== 'IMG') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (imgElm.hasAttribute('data-mce-object') || imgElm.hasAttribute('data-mce-placeholder'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the upload loader and disables UI elements while loading a file.
|
||||
*/
|
||||
startImageLoading() {
|
||||
showElements(Selectors.IMAGE.elements.loaderIcon, this.root);
|
||||
hideElements(Selectors.IMAGE.elements.insertImage, this.root);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the upload loader and disables UI elements while loading a file.
|
||||
*/
|
||||
stopImageLoading() {
|
||||
hideElements(Selectors.IMAGE.elements.loaderIcon, this.root);
|
||||
showElements(Selectors.IMAGE.elements.insertImage, this.root);
|
||||
}
|
||||
}
|
||||
614
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/imagedetails.js
Executable file
614
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/imagedetails.js
Executable file
@@ -0,0 +1,614 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Tiny media plugin image details class for Moodle.
|
||||
*
|
||||
* @module tiny_mediacms/imagedetails
|
||||
* @copyright 2024 Meirza <meirza.arson@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
import Config from 'core/config';
|
||||
import ModalEvents from 'core/modal_events';
|
||||
import Notification from 'core/notification';
|
||||
import Pending from 'core/pending';
|
||||
import Selectors from './selectors';
|
||||
import Templates from 'core/templates';
|
||||
import {getString} from 'core/str';
|
||||
import {ImageInsert} from 'tiny_mediacms/imageinsert';
|
||||
import {
|
||||
bodyImageInsert,
|
||||
footerImageInsert,
|
||||
showElements,
|
||||
hideElements,
|
||||
isPercentageValue,
|
||||
} from 'tiny_mediacms/imagehelpers';
|
||||
|
||||
export class ImageDetails {
|
||||
DEFAULTS = {
|
||||
WIDTH: 160,
|
||||
HEIGHT: 160,
|
||||
};
|
||||
|
||||
rawImageDimensions = null;
|
||||
|
||||
constructor(
|
||||
root,
|
||||
editor,
|
||||
currentModal,
|
||||
canShowFilePicker,
|
||||
canShowDropZone,
|
||||
currentUrl,
|
||||
image,
|
||||
) {
|
||||
this.root = root;
|
||||
this.editor = editor;
|
||||
this.currentModal = currentModal;
|
||||
this.canShowFilePicker = canShowFilePicker;
|
||||
this.canShowDropZone = canShowDropZone;
|
||||
this.currentUrl = currentUrl;
|
||||
this.image = image;
|
||||
}
|
||||
|
||||
init = function() {
|
||||
this.currentModal.setTitle(getString('imagedetails', 'tiny_mediacms'));
|
||||
this.imageTypeChecked();
|
||||
this.presentationChanged();
|
||||
this.storeImageDimensions(this.image);
|
||||
this.setImageDimensions();
|
||||
this.registerEventListeners();
|
||||
};
|
||||
|
||||
/**
|
||||
* Loads and displays a preview image based on the provided URL, and handles image loading events.
|
||||
*/
|
||||
loadInsertImage = async function() {
|
||||
const templateContext = {
|
||||
elementid: this.editor.id,
|
||||
showfilepicker: this.canShowFilePicker,
|
||||
showdropzone: this.canShowDropZone,
|
||||
};
|
||||
|
||||
Promise.all([bodyImageInsert(templateContext, this.root), footerImageInsert(templateContext, this.root)])
|
||||
.then(() => {
|
||||
const imageinsert = new ImageInsert(
|
||||
this.root,
|
||||
this.editor,
|
||||
this.currentModal,
|
||||
this.canShowFilePicker,
|
||||
this.canShowDropZone,
|
||||
);
|
||||
imageinsert.init();
|
||||
return;
|
||||
})
|
||||
.catch(error => {
|
||||
window.console.log(error);
|
||||
});
|
||||
};
|
||||
|
||||
storeImageDimensions(image) {
|
||||
// Store dimensions of the raw image, falling back to defaults for images without dimensions (e.g. SVG).
|
||||
this.rawImageDimensions = {
|
||||
width: image.width || this.DEFAULTS.WIDTH,
|
||||
height: image.height || this.DEFAULTS.HEIGHT,
|
||||
};
|
||||
|
||||
const getCurrentWidth = (element) => {
|
||||
if (element.value === '') {
|
||||
element.value = this.rawImageDimensions.width;
|
||||
}
|
||||
return element.value;
|
||||
};
|
||||
|
||||
const getCurrentHeight = (element) => {
|
||||
if (element.value === '') {
|
||||
element.value = this.rawImageDimensions.height;
|
||||
}
|
||||
return element.value;
|
||||
};
|
||||
|
||||
const widthInput = this.root.querySelector(Selectors.IMAGE.elements.width);
|
||||
const currentWidth = getCurrentWidth(widthInput);
|
||||
|
||||
const heightInput = this.root.querySelector(Selectors.IMAGE.elements.height);
|
||||
const currentHeight = getCurrentHeight(heightInput);
|
||||
|
||||
const preview = this.root.querySelector(Selectors.IMAGE.elements.preview);
|
||||
preview.setAttribute('src', image.src);
|
||||
preview.style.display = '';
|
||||
|
||||
// Ensure the checkbox always in unchecked status when an image loads at first.
|
||||
const constrain = this.root.querySelector(Selectors.IMAGE.elements.constrain);
|
||||
if (isPercentageValue(currentWidth) && isPercentageValue(currentHeight)) {
|
||||
constrain.checked = currentWidth === currentHeight;
|
||||
} else if (image.width === 0 || image.height === 0) {
|
||||
// If we don't have both dimensions of the image, we can't auto-size it, so disable control.
|
||||
constrain.disabled = 'disabled';
|
||||
} else {
|
||||
// This is the same as comparing to 3 decimal places.
|
||||
const widthRatio = Math.round(100 * parseInt(currentWidth, 10) / image.width);
|
||||
const heightRatio = Math.round(100 * parseInt(currentHeight, 10) / image.height);
|
||||
constrain.checked = widthRatio === heightRatio;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the selected size option based on current width and height values.
|
||||
*
|
||||
* @param {number} currentWidth - The current width value.
|
||||
* @param {number} currentHeight - The current height value.
|
||||
*/
|
||||
const setSelectedSize = (currentWidth, currentHeight) => {
|
||||
if (this.rawImageDimensions.width === currentWidth &&
|
||||
this.rawImageDimensions.height === currentHeight
|
||||
) {
|
||||
this.currentWidth = this.rawImageDimensions.width;
|
||||
this.currentHeight = this.rawImageDimensions.height;
|
||||
this.sizeChecked('original');
|
||||
} else {
|
||||
this.currentWidth = currentWidth;
|
||||
this.currentHeight = currentHeight;
|
||||
this.sizeChecked('custom');
|
||||
}
|
||||
};
|
||||
|
||||
setSelectedSize(Number(currentWidth), Number(currentHeight));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the selection of image size options and updates the form inputs accordingly.
|
||||
*
|
||||
* @param {string} option - The selected image size option ("original" or "custom").
|
||||
*/
|
||||
sizeChecked(option) {
|
||||
const widthInput = this.root.querySelector(Selectors.IMAGE.elements.width);
|
||||
const heightInput = this.root.querySelector(Selectors.IMAGE.elements.height);
|
||||
if (option === "original") {
|
||||
this.sizeOriginalChecked();
|
||||
widthInput.value = this.rawImageDimensions.width;
|
||||
heightInput.value = this.rawImageDimensions.height;
|
||||
} else if (option === "custom") {
|
||||
this.sizeCustomChecked();
|
||||
widthInput.value = this.currentWidth;
|
||||
heightInput.value = this.currentHeight;
|
||||
|
||||
// If the current size is equal to the original size, then check the Keep proportion checkbox.
|
||||
if (this.currentWidth === this.rawImageDimensions.width && this.currentHeight === this.rawImageDimensions.height) {
|
||||
const constrainField = this.root.querySelector(Selectors.IMAGE.elements.constrain);
|
||||
constrainField.checked = true;
|
||||
}
|
||||
}
|
||||
this.autoAdjustSize();
|
||||
}
|
||||
|
||||
autoAdjustSize(forceHeight = false) {
|
||||
// If we do not know the image size, do not do anything.
|
||||
if (!this.rawImageDimensions) {
|
||||
return;
|
||||
}
|
||||
|
||||
const widthField = this.root.querySelector(Selectors.IMAGE.elements.width);
|
||||
const heightField = this.root.querySelector(Selectors.IMAGE.elements.height);
|
||||
|
||||
const normalizeFieldData = (fieldData) => {
|
||||
fieldData.isPercentageValue = !!isPercentageValue(fieldData.field.value);
|
||||
if (fieldData.isPercentageValue) {
|
||||
fieldData.percentValue = parseInt(fieldData.field.value, 10);
|
||||
fieldData.pixelSize = this.rawImageDimensions[fieldData.type] / 100 * fieldData.percentValue;
|
||||
} else {
|
||||
fieldData.pixelSize = parseInt(fieldData.field.value, 10);
|
||||
fieldData.percentValue = fieldData.pixelSize / this.rawImageDimensions[fieldData.type] * 100;
|
||||
}
|
||||
|
||||
return fieldData;
|
||||
};
|
||||
|
||||
const getKeyField = () => {
|
||||
const getValue = () => {
|
||||
if (forceHeight) {
|
||||
return {
|
||||
field: heightField,
|
||||
type: 'height',
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
field: widthField,
|
||||
type: 'width',
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const currentValue = getValue();
|
||||
if (currentValue.field.value === '') {
|
||||
currentValue.field.value = this.rawImageDimensions[currentValue.type];
|
||||
}
|
||||
|
||||
return normalizeFieldData(currentValue);
|
||||
};
|
||||
|
||||
const getRelativeField = () => {
|
||||
if (forceHeight) {
|
||||
return normalizeFieldData({
|
||||
field: widthField,
|
||||
type: 'width',
|
||||
});
|
||||
} else {
|
||||
return normalizeFieldData({
|
||||
field: heightField,
|
||||
type: 'height',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Now update with the new values.
|
||||
const constrainField = this.root.querySelector(Selectors.IMAGE.elements.constrain);
|
||||
if (constrainField.checked) {
|
||||
const keyField = getKeyField();
|
||||
const relativeField = getRelativeField();
|
||||
// We are keeping the image in proportion.
|
||||
// Calculate the size for the relative field.
|
||||
if (keyField.isPercentageValue) {
|
||||
// In proportion, so the percentages are the same.
|
||||
relativeField.field.value = keyField.field.value;
|
||||
relativeField.percentValue = keyField.percentValue;
|
||||
} else {
|
||||
relativeField.pixelSize = Math.round(
|
||||
keyField.pixelSize / this.rawImageDimensions[keyField.type] * this.rawImageDimensions[relativeField.type]
|
||||
);
|
||||
relativeField.field.value = relativeField.pixelSize;
|
||||
}
|
||||
}
|
||||
|
||||
// Store the custom width and height to reuse.
|
||||
this.currentWidth = Number(widthField.value) !== this.rawImageDimensions.width ? widthField.value : this.currentWidth;
|
||||
this.currentHeight = Number(heightField.value) !== this.rawImageDimensions.height ? heightField.value : this.currentHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the dimensions of the image preview element based on user input and constraints.
|
||||
*/
|
||||
setImageDimensions = () => {
|
||||
const imagePreviewBox = this.root.querySelector(Selectors.IMAGE.elements.previewBox);
|
||||
const image = this.root.querySelector(Selectors.IMAGE.elements.preview);
|
||||
const widthField = this.root.querySelector(Selectors.IMAGE.elements.width);
|
||||
const heightField = this.root.querySelector(Selectors.IMAGE.elements.height);
|
||||
|
||||
const updateImageDimensions = () => {
|
||||
// Get the latest dimensions of the preview box for responsiveness.
|
||||
const boxWidth = imagePreviewBox.clientWidth;
|
||||
const boxHeight = imagePreviewBox.clientHeight;
|
||||
// Get the new width and height for the image.
|
||||
const dimensions = this.fitSquareIntoBox(widthField.value, heightField.value, boxWidth, boxHeight);
|
||||
image.style.width = `${dimensions.width}px`;
|
||||
image.style.height = `${dimensions.height}px`;
|
||||
};
|
||||
// If the client size is zero, then get the new dimensions once the modal is shown.
|
||||
if (imagePreviewBox.clientWidth === 0) {
|
||||
// Call the shown event.
|
||||
this.currentModal.getRoot().on(ModalEvents.shown, () => {
|
||||
updateImageDimensions();
|
||||
});
|
||||
} else {
|
||||
updateImageDimensions();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles the selection of the "Original Size" option and updates the form elements accordingly.
|
||||
*/
|
||||
sizeOriginalChecked() {
|
||||
this.root.querySelector(Selectors.IMAGE.elements.sizeOriginal).checked = true;
|
||||
this.root.querySelector(Selectors.IMAGE.elements.sizeCustom).checked = false;
|
||||
hideElements(Selectors.IMAGE.elements.properties, this.root);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the selection of the "Custom Size" option and updates the form elements accordingly.
|
||||
*/
|
||||
sizeCustomChecked() {
|
||||
this.root.querySelector(Selectors.IMAGE.elements.sizeOriginal).checked = false;
|
||||
this.root.querySelector(Selectors.IMAGE.elements.sizeCustom).checked = true;
|
||||
showElements(Selectors.IMAGE.elements.properties, this.root);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles changes in the image presentation checkbox and enables/disables the image alt text input accordingly.
|
||||
*/
|
||||
presentationChanged() {
|
||||
const presentation = this.root.querySelector(Selectors.IMAGE.elements.presentation);
|
||||
const alt = this.root.querySelector(Selectors.IMAGE.elements.alt);
|
||||
alt.disabled = presentation.checked;
|
||||
|
||||
// Counting the image description characters.
|
||||
this.handleKeyupCharacterCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* This function checks whether an image URL is local (within the same website's domain) or external (from an external source).
|
||||
* Depending on the result, it dynamically updates the visibility and content of HTML elements in a user interface.
|
||||
* If the image is local then we only show it's filename.
|
||||
* If the image is external then it will show full URL and it can be updated.
|
||||
*/
|
||||
imageTypeChecked() {
|
||||
const regex = new RegExp(`${Config.wwwroot}`);
|
||||
|
||||
// True if the URL is from external, otherwise false.
|
||||
const isExternalUrl = regex.test(this.currentUrl) === false;
|
||||
|
||||
// Hide the URL input.
|
||||
hideElements(Selectors.IMAGE.elements.url, this.root);
|
||||
|
||||
if (!isExternalUrl) {
|
||||
// Split the URL by '/' to get an array of segments.
|
||||
const segments = this.currentUrl.split('/');
|
||||
// Get the last segment, which should be the filename.
|
||||
const filename = segments.pop().split('?')[0];
|
||||
// Show the file name.
|
||||
this.setFilenameLabel(decodeURI(filename));
|
||||
} else {
|
||||
|
||||
this.setFilenameLabel(decodeURI(this.currentUrl));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the string for the URL label element.
|
||||
*
|
||||
* @param {string} label - The label text to set.
|
||||
*/
|
||||
setFilenameLabel(label) {
|
||||
const urlLabelEle = this.root.querySelector(Selectors.IMAGE.elements.fileNameLabel);
|
||||
if (urlLabelEle) {
|
||||
urlLabelEle.innerHTML = label;
|
||||
urlLabelEle.setAttribute("title", label);
|
||||
}
|
||||
}
|
||||
|
||||
toggleAriaInvalid(selectors, predicate) {
|
||||
selectors.forEach((selector) => {
|
||||
const elements = this.root.querySelectorAll(selector);
|
||||
elements.forEach((element) => element.setAttribute('aria-invalid', predicate));
|
||||
});
|
||||
}
|
||||
|
||||
hasErrorUrlField() {
|
||||
const urlError = this.currentUrl === '';
|
||||
if (urlError) {
|
||||
showElements(Selectors.IMAGE.elements.urlWarning, this.root);
|
||||
} else {
|
||||
hideElements(Selectors.IMAGE.elements.urlWarning, this.root);
|
||||
}
|
||||
this.toggleAriaInvalid([Selectors.IMAGE.elements.url], urlError);
|
||||
|
||||
return urlError;
|
||||
}
|
||||
|
||||
hasErrorAltField() {
|
||||
const alt = this.root.querySelector(Selectors.IMAGE.elements.alt).value;
|
||||
const presentation = this.root.querySelector(Selectors.IMAGE.elements.presentation).checked;
|
||||
const imageAltError = alt === '' && !presentation;
|
||||
if (imageAltError) {
|
||||
showElements(Selectors.IMAGE.elements.altWarning, this.root);
|
||||
} else {
|
||||
hideElements(Selectors.IMAGE.elements.urlWaaltWarningrning, this.root);
|
||||
}
|
||||
this.toggleAriaInvalid([Selectors.IMAGE.elements.alt, Selectors.IMAGE.elements.presentation], imageAltError);
|
||||
|
||||
return imageAltError;
|
||||
}
|
||||
|
||||
updateWarning() {
|
||||
const urlError = this.hasErrorUrlField();
|
||||
const imageAltError = this.hasErrorAltField();
|
||||
|
||||
return urlError || imageAltError;
|
||||
}
|
||||
|
||||
getImageContext() {
|
||||
// Check if there are any accessibility issues.
|
||||
if (this.updateWarning()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const classList = [];
|
||||
const constrain = this.root.querySelector(Selectors.IMAGE.elements.constrain).checked;
|
||||
const sizeOriginal = this.root.querySelector(Selectors.IMAGE.elements.sizeOriginal).checked;
|
||||
if (constrain || sizeOriginal) {
|
||||
// If the Auto size checkbox is checked or the Original size is checked, then apply the responsive class.
|
||||
classList.push(Selectors.IMAGE.styles.responsive);
|
||||
} else {
|
||||
// Otherwise, remove it.
|
||||
classList.pop(Selectors.IMAGE.styles.responsive);
|
||||
}
|
||||
|
||||
return {
|
||||
url: this.currentUrl,
|
||||
alt: this.root.querySelector(Selectors.IMAGE.elements.alt).value,
|
||||
width: this.root.querySelector(Selectors.IMAGE.elements.width).value,
|
||||
height: this.root.querySelector(Selectors.IMAGE.elements.height).value,
|
||||
presentation: this.root.querySelector(Selectors.IMAGE.elements.presentation).checked,
|
||||
customStyle: this.root.querySelector(Selectors.IMAGE.elements.customStyle).value,
|
||||
classlist: classList.join(' '),
|
||||
};
|
||||
}
|
||||
|
||||
setImage() {
|
||||
const pendingPromise = new Pending('tiny_mediacms:setImage');
|
||||
const url = this.currentUrl;
|
||||
if (url === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if there are any accessibility issues.
|
||||
if (this.updateWarning()) {
|
||||
pendingPromise.resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for invalid width or height.
|
||||
const width = this.root.querySelector(Selectors.IMAGE.elements.width).value;
|
||||
if (!isPercentageValue(width) && isNaN(parseInt(width, 10))) {
|
||||
this.root.querySelector(Selectors.IMAGE.elements.width).focus();
|
||||
pendingPromise.resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
const height = this.root.querySelector(Selectors.IMAGE.elements.height).value;
|
||||
if (!isPercentageValue(height) && isNaN(parseInt(height, 10))) {
|
||||
this.root.querySelector(Selectors.IMAGE.elements.height).focus();
|
||||
pendingPromise.resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
Templates.render('tiny_mediacms/image', this.getImageContext())
|
||||
.then((html) => {
|
||||
this.editor.insertContent(html);
|
||||
this.currentModal.destroy();
|
||||
pendingPromise.resolve();
|
||||
|
||||
return html;
|
||||
})
|
||||
.catch(error => {
|
||||
window.console.log(error);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the image after confirming with the user and loads the insert image page.
|
||||
*/
|
||||
deleteImage() {
|
||||
Notification.deleteCancelPromise(
|
||||
getString('deleteimage', 'tiny_mediacms'),
|
||||
getString('deleteimagewarning', 'tiny_mediacms'),
|
||||
).then(() => {
|
||||
hideElements(Selectors.IMAGE.elements.altWarning, this.root);
|
||||
// Removing the image in the preview will bring the user to the insert page.
|
||||
this.loadInsertImage();
|
||||
return;
|
||||
}).catch(error => {
|
||||
window.console.log(error);
|
||||
});
|
||||
}
|
||||
|
||||
registerEventListeners() {
|
||||
const submitAction = this.root.querySelector(Selectors.IMAGE.actions.submit);
|
||||
submitAction.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
this.setImage();
|
||||
});
|
||||
|
||||
const deleteImageEle = this.root.querySelector(Selectors.IMAGE.actions.deleteImage);
|
||||
deleteImageEle.addEventListener('click', () => {
|
||||
this.deleteImage();
|
||||
});
|
||||
deleteImageEle.addEventListener("keydown", (e) => {
|
||||
if (e.key === "Enter") {
|
||||
this.deleteImage();
|
||||
}
|
||||
});
|
||||
|
||||
this.root.addEventListener('change', (e) => {
|
||||
const presentationEle = e.target.closest(Selectors.IMAGE.elements.presentation);
|
||||
if (presentationEle) {
|
||||
this.presentationChanged();
|
||||
}
|
||||
|
||||
const constrainEle = e.target.closest(Selectors.IMAGE.elements.constrain);
|
||||
if (constrainEle) {
|
||||
this.autoAdjustSize();
|
||||
}
|
||||
|
||||
const sizeOriginalEle = e.target.closest(Selectors.IMAGE.elements.sizeOriginal);
|
||||
if (sizeOriginalEle) {
|
||||
this.sizeChecked('original');
|
||||
}
|
||||
|
||||
const sizeCustomEle = e.target.closest(Selectors.IMAGE.elements.sizeCustom);
|
||||
if (sizeCustomEle) {
|
||||
this.sizeChecked('custom');
|
||||
}
|
||||
});
|
||||
|
||||
this.root.addEventListener('blur', (e) => {
|
||||
if (e.target.nodeType === Node.ELEMENT_NODE) {
|
||||
|
||||
const presentationEle = e.target.closest(Selectors.IMAGE.elements.presentation);
|
||||
if (presentationEle) {
|
||||
this.presentationChanged();
|
||||
}
|
||||
}
|
||||
}, true);
|
||||
|
||||
// Character count.
|
||||
this.root.addEventListener('keyup', (e) => {
|
||||
const altEle = e.target.closest(Selectors.IMAGE.elements.alt);
|
||||
if (altEle) {
|
||||
this.handleKeyupCharacterCount();
|
||||
}
|
||||
});
|
||||
|
||||
this.root.addEventListener('input', (e) => {
|
||||
const widthEle = e.target.closest(Selectors.IMAGE.elements.width);
|
||||
if (widthEle) {
|
||||
// Avoid empty value.
|
||||
widthEle.value = widthEle.value === "" ? 0 : Number(widthEle.value);
|
||||
this.autoAdjustSize();
|
||||
}
|
||||
|
||||
const heightEle = e.target.closest(Selectors.IMAGE.elements.height);
|
||||
if (heightEle) {
|
||||
// Avoid empty value.
|
||||
heightEle.value = heightEle.value === "" ? 0 : Number(heightEle.value);
|
||||
this.autoAdjustSize(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
handleKeyupCharacterCount() {
|
||||
const alt = this.root.querySelector(Selectors.IMAGE.elements.alt).value;
|
||||
const current = this.root.querySelector('#currentcount');
|
||||
current.innerHTML = alt.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the dimensions to fit a square into a specified box while maintaining aspect ratio.
|
||||
*
|
||||
* @param {number} squareWidth - The width of the square.
|
||||
* @param {number} squareHeight - The height of the square.
|
||||
* @param {number} boxWidth - The width of the box.
|
||||
* @param {number} boxHeight - The height of the box.
|
||||
* @returns {Object} An object with the new width and height of the square to fit in the box.
|
||||
*/
|
||||
fitSquareIntoBox = (squareWidth, squareHeight, boxWidth, boxHeight) => {
|
||||
if (squareWidth < boxWidth && squareHeight < boxHeight) {
|
||||
// If the square is smaller than the box, keep its dimensions.
|
||||
return {
|
||||
width: squareWidth,
|
||||
height: squareHeight,
|
||||
};
|
||||
}
|
||||
// Calculate the scaling factor based on the minimum scaling required to fit in the box.
|
||||
const widthScaleFactor = boxWidth / squareWidth;
|
||||
const heightScaleFactor = boxHeight / squareHeight;
|
||||
const minScaleFactor = Math.min(widthScaleFactor, heightScaleFactor);
|
||||
// Scale the square's dimensions based on the aspect ratio and the minimum scaling factor.
|
||||
const newWidth = squareWidth * minScaleFactor;
|
||||
const newHeight = squareHeight * minScaleFactor;
|
||||
return {
|
||||
width: newWidth,
|
||||
height: newHeight,
|
||||
};
|
||||
};
|
||||
}
|
||||
149
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/imagehelpers.js
Executable file
149
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/imagehelpers.js
Executable file
@@ -0,0 +1,149 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Tiny media plugin image helpers.
|
||||
*
|
||||
* @module tiny_mediacms/imagehelpers
|
||||
* @copyright 2024 Meirza <meirza.arson@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
import Templates from 'core/templates';
|
||||
|
||||
/**
|
||||
* Renders and inserts the body template for inserting an image into the modal.
|
||||
*
|
||||
* @param {object} templateContext - The context for rendering the template.
|
||||
* @param {HTMLElement} root - The root element where the template will be inserted.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export const bodyImageInsert = async(templateContext, root) => {
|
||||
return Templates.renderForPromise('tiny_mediacms/insert_image_modal_insert', {...templateContext})
|
||||
.then(({html, js}) => {
|
||||
Templates.replaceNodeContents(root.querySelector('.tiny_imagecms_body_template'), html, js);
|
||||
return;
|
||||
})
|
||||
.catch(error => {
|
||||
window.console.log(error);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Renders and inserts the footer template for inserting an image into the modal.
|
||||
*
|
||||
* @param {object} templateContext - The context for rendering the template.
|
||||
* @param {HTMLElement} root - The root element where the template will be inserted.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export const footerImageInsert = async(templateContext, root) => {
|
||||
return Templates.renderForPromise('tiny_mediacms/insert_image_modal_insert_footer', {...templateContext})
|
||||
.then(({html, js}) => {
|
||||
Templates.replaceNodeContents(root.querySelector('.tiny_imagecms_footer_template'), html, js);
|
||||
return;
|
||||
})
|
||||
.catch(error => {
|
||||
window.console.log(error);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Renders and inserts the body template for displaying image details in the modal.
|
||||
*
|
||||
* @param {object} templateContext - The context for rendering the template.
|
||||
* @param {HTMLElement} root - The root element where the template will be inserted.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export const bodyImageDetails = async(templateContext, root) => {
|
||||
return Templates.renderForPromise('tiny_mediacms/insert_image_modal_details', {...templateContext})
|
||||
.then(({html, js}) => {
|
||||
Templates.replaceNodeContents(root.querySelector('.tiny_imagecms_body_template'), html, js);
|
||||
return;
|
||||
})
|
||||
.catch(error => {
|
||||
window.console.log(error);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Renders and inserts the footer template for displaying image details in the modal.
|
||||
* @param {object} templateContext - The context for rendering the template.
|
||||
* @param {HTMLElement} root - The root element where the template will be inserted.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export const footerImageDetails = async(templateContext, root) => {
|
||||
return Templates.renderForPromise('tiny_mediacms/insert_image_modal_details_footer', {...templateContext})
|
||||
.then(({html, js}) => {
|
||||
Templates.replaceNodeContents(root.querySelector('.tiny_imagecms_footer_template'), html, js);
|
||||
return;
|
||||
})
|
||||
.catch(error => {
|
||||
window.console.log(error);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Show the element(s).
|
||||
*
|
||||
* @param {string|string[]} elements - The CSS selector for the elements to toggle.
|
||||
* @param {object} root - The CSS selector for the elements to toggle.
|
||||
*/
|
||||
export const showElements = (elements, root) => {
|
||||
if (elements instanceof Array) {
|
||||
elements.forEach((elementSelector) => {
|
||||
const element = root.querySelector(elementSelector);
|
||||
if (element) {
|
||||
element.classList.remove('d-none');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
const element = root.querySelector(elements);
|
||||
if (element) {
|
||||
element.classList.remove('d-none');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Hide the element(s).
|
||||
*
|
||||
* @param {string|string[]} elements - The CSS selector for the elements to toggle.
|
||||
* @param {object} root - The CSS selector for the elements to toggle.
|
||||
*/
|
||||
export const hideElements = (elements, root) => {
|
||||
if (elements instanceof Array) {
|
||||
elements.forEach((elementSelector) => {
|
||||
const element = root.querySelector(elementSelector);
|
||||
if (element) {
|
||||
element.classList.add('d-none');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
const element = root.querySelector(elements);
|
||||
if (element) {
|
||||
element.classList.add('d-none');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if the given value is a percentage value.
|
||||
*
|
||||
* @param {string} value - The value to check.
|
||||
* @returns {boolean} True if the value is a percentage value, false otherwise.
|
||||
*/
|
||||
export const isPercentageValue = (value) => {
|
||||
return value.match(/\d+%/);
|
||||
};
|
||||
282
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/imageinsert.js
Executable file
282
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/imageinsert.js
Executable file
@@ -0,0 +1,282 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Tiny media plugin image insertion class for Moodle.
|
||||
*
|
||||
* @module tiny_mediacms/imageinsert
|
||||
* @copyright 2024 Meirza <meirza.arson@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
import Selectors from './selectors';
|
||||
import Dropzone from 'core/dropzone';
|
||||
import uploadFile from 'editor_tiny/uploader';
|
||||
import {prefetchStrings} from 'core/prefetch';
|
||||
import {getStrings} from 'core/str';
|
||||
import {component} from "./common";
|
||||
import {getFilePicker} from 'editor_tiny/options';
|
||||
import {displayFilepicker} from 'editor_tiny/utils';
|
||||
import {ImageDetails} from 'tiny_mediacms/imagedetails';
|
||||
import {
|
||||
showElements,
|
||||
hideElements,
|
||||
bodyImageDetails,
|
||||
footerImageDetails,
|
||||
} from 'tiny_mediacms/imagehelpers';
|
||||
|
||||
prefetchStrings('tiny_mediacms', [
|
||||
'insertimage',
|
||||
'enterurl',
|
||||
'enterurlor',
|
||||
'imageurlrequired',
|
||||
'uploading',
|
||||
'loading',
|
||||
'addfilesdrop',
|
||||
'sizecustom_help',
|
||||
]);
|
||||
|
||||
export class ImageInsert {
|
||||
|
||||
constructor(
|
||||
root,
|
||||
editor,
|
||||
currentModal,
|
||||
canShowFilePicker,
|
||||
canShowDropZone,
|
||||
) {
|
||||
this.root = root;
|
||||
this.editor = editor;
|
||||
this.currentModal = currentModal;
|
||||
this.canShowFilePicker = canShowFilePicker;
|
||||
this.canShowDropZone = canShowDropZone;
|
||||
}
|
||||
|
||||
init = async function() {
|
||||
// Get the localization lang strings and turn them into object.
|
||||
const langStringKeys = [
|
||||
'insertimage',
|
||||
'enterurl',
|
||||
'enterurlor',
|
||||
'imageurlrequired',
|
||||
'uploading',
|
||||
'loading',
|
||||
'addfilesdrop',
|
||||
'sizecustom_help',
|
||||
];
|
||||
const langStringvalues = await getStrings([...langStringKeys].map((key) => ({key, component})));
|
||||
|
||||
// Convert array to object.
|
||||
this.langStrings = Object.fromEntries(langStringKeys.map((key, index) => [key, langStringvalues[index]]));
|
||||
this.currentModal.setTitle(this.langStrings.insertimage);
|
||||
if (this.canShowDropZone) {
|
||||
const dropZoneEle = document.querySelector(Selectors.IMAGE.elements.dropzoneContainer);
|
||||
|
||||
// Accepted types can be either a string or an array.
|
||||
let acceptedTypes = getFilePicker(this.editor, 'image').accepted_types;
|
||||
if (Array.isArray(acceptedTypes)) {
|
||||
acceptedTypes = acceptedTypes.join(',');
|
||||
}
|
||||
|
||||
const dropZone = new Dropzone(
|
||||
dropZoneEle,
|
||||
acceptedTypes,
|
||||
files => {
|
||||
this.handleUploadedFile(files);
|
||||
}
|
||||
);
|
||||
dropZone.setLabel(this.langStrings.addfilesdrop);
|
||||
dropZone.init();
|
||||
}
|
||||
await this.registerEventListeners();
|
||||
};
|
||||
|
||||
/**
|
||||
* Enables or disables the URL-related buttons in the footer based on the current URL and input value.
|
||||
*/
|
||||
toggleUrlButton() {
|
||||
const urlInput = this.root.querySelector(Selectors.IMAGE.elements.url);
|
||||
const url = urlInput.value;
|
||||
const addUrl = this.root.querySelector(Selectors.IMAGE.actions.addUrl);
|
||||
addUrl.disabled = !(url !== "" && this.isValidUrl(url));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if given string is a valid URL.
|
||||
*
|
||||
* @param {String} urlString URL the link will point to.
|
||||
* @returns {boolean} True is valid, otherwise false.
|
||||
*/
|
||||
isValidUrl = urlString => {
|
||||
const urlPattern = 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})|localhost)' + // OR ip (v4) address, localhost.
|
||||
'(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*'); // Port and path.
|
||||
return !!urlPattern.test(urlString);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles changes in the image URL input field and loads a preview of the image if the URL has changed.
|
||||
*/
|
||||
urlChanged() {
|
||||
hideElements(Selectors.IMAGE.elements.urlWarning, this.root);
|
||||
const input = this.root.querySelector(Selectors.IMAGE.elements.url);
|
||||
if (input.value && input.value !== this.currentUrl) {
|
||||
this.loadPreviewImage(input.value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads and displays a preview image based on the provided URL, and handles image loading events.
|
||||
*
|
||||
* @param {string} url - The URL of the image to load and display.
|
||||
*/
|
||||
loadPreviewImage = function(url) {
|
||||
this.startImageLoading();
|
||||
this.currentUrl = url;
|
||||
const image = new Image();
|
||||
image.src = url;
|
||||
image.addEventListener('error', () => {
|
||||
const urlWarningLabelEle = this.root.querySelector(Selectors.IMAGE.elements.urlWarning);
|
||||
urlWarningLabelEle.innerHTML = this.langStrings.imageurlrequired;
|
||||
showElements(Selectors.IMAGE.elements.urlWarning, this.root);
|
||||
this.currentUrl = "";
|
||||
this.stopImageLoading();
|
||||
});
|
||||
|
||||
image.addEventListener('load', () => {
|
||||
let templateContext = {};
|
||||
templateContext.sizecustomhelpicon = {text: this.langStrings.sizecustom_help};
|
||||
Promise.all([bodyImageDetails(templateContext, this.root), footerImageDetails(templateContext, this.root)])
|
||||
.then(() => {
|
||||
const imagedetails = new ImageDetails(
|
||||
this.root,
|
||||
this.editor,
|
||||
this.currentModal,
|
||||
this.canShowFilePicker,
|
||||
this.canShowDropZone,
|
||||
this.currentUrl,
|
||||
image,
|
||||
);
|
||||
imagedetails.init();
|
||||
return;
|
||||
}).then(() => {
|
||||
this.stopImageLoading();
|
||||
return;
|
||||
})
|
||||
.catch(error => {
|
||||
window.console.log(error);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Displays the upload loader and disables UI elements while loading a file.
|
||||
*/
|
||||
startImageLoading() {
|
||||
showElements(Selectors.IMAGE.elements.loaderIcon, this.root);
|
||||
const elementsToHide = [
|
||||
Selectors.IMAGE.elements.insertImage,
|
||||
Selectors.IMAGE.elements.urlWarning,
|
||||
Selectors.IMAGE.elements.modalFooter,
|
||||
];
|
||||
hideElements(elementsToHide, this.root);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the upload loader and disables UI elements while loading a file.
|
||||
*/
|
||||
stopImageLoading() {
|
||||
hideElements(Selectors.IMAGE.elements.loaderIcon, this.root);
|
||||
const elementsToShow = [
|
||||
Selectors.IMAGE.elements.insertImage,
|
||||
Selectors.IMAGE.elements.modalFooter,
|
||||
];
|
||||
showElements(elementsToShow, this.root);
|
||||
}
|
||||
|
||||
filePickerCallback(params) {
|
||||
if (params.url) {
|
||||
this.loadPreviewImage(params.url);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the content of the loader icon.
|
||||
*
|
||||
* @param {HTMLElement} root - The root element containing the loader icon.
|
||||
* @param {object} langStrings - An object containing language strings.
|
||||
* @param {number|null} progress - The progress percentage (optional).
|
||||
* @returns {void}
|
||||
*/
|
||||
updateLoaderIcon = (root, langStrings, progress = null) => {
|
||||
const loaderIcon = root.querySelector(Selectors.IMAGE.elements.loaderIconContainer + ' div');
|
||||
loaderIcon.innerHTML = progress !== null ? `${langStrings.uploading} ${Math.round(progress)}%` : langStrings.loading;
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles the uploaded file, initiates the upload process, and updates the UI during the upload.
|
||||
*
|
||||
* @param {FileList} files - The list of files to upload (usually from a file input field).
|
||||
* @returns {Promise<void>} A promise that resolves when the file is uploaded and processed.
|
||||
*/
|
||||
handleUploadedFile = async(files) => {
|
||||
try {
|
||||
this.startImageLoading();
|
||||
const fileURL = await uploadFile(this.editor, 'image', files[0], files[0].name, (progress) => {
|
||||
this.updateLoaderIcon(this.root, this.langStrings, progress);
|
||||
});
|
||||
// Set the loader icon content to "loading" after the file upload completes.
|
||||
this.updateLoaderIcon(this.root, this.langStrings);
|
||||
this.filePickerCallback({url: fileURL});
|
||||
} catch (error) {
|
||||
// Handle the error.
|
||||
const urlWarningLabelEle = this.root.querySelector(Selectors.IMAGE.elements.urlWarning);
|
||||
urlWarningLabelEle.innerHTML = error.error !== undefined ? error.error : error;
|
||||
showElements(Selectors.IMAGE.elements.urlWarning, this.root);
|
||||
this.stopImageLoading();
|
||||
}
|
||||
};
|
||||
|
||||
registerEventListeners() {
|
||||
this.root.addEventListener('click', async(e) => {
|
||||
const addUrlEle = e.target.closest(Selectors.IMAGE.actions.addUrl);
|
||||
if (addUrlEle) {
|
||||
this.urlChanged();
|
||||
}
|
||||
|
||||
const imageBrowserAction = e.target.closest(Selectors.IMAGE.actions.imageBrowser);
|
||||
if (imageBrowserAction && this.canShowFilePicker) {
|
||||
e.preventDefault();
|
||||
const params = await displayFilepicker(this.editor, 'image');
|
||||
this.filePickerCallback(params);
|
||||
}
|
||||
});
|
||||
|
||||
this.root.addEventListener('input', (e) => {
|
||||
const urlEle = e.target.closest(Selectors.IMAGE.elements.url);
|
||||
if (urlEle) {
|
||||
this.toggleUrlButton();
|
||||
}
|
||||
});
|
||||
|
||||
const fileInput = this.root.querySelector(Selectors.IMAGE.elements.fileInput);
|
||||
if (fileInput) {
|
||||
fileInput.addEventListener('change', () => {
|
||||
this.handleUploadedFile(fileInput.files);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
49
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/imagemodal.js
Executable file
49
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/imagemodal.js
Executable file
@@ -0,0 +1,49 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Image Modal for Tiny.
|
||||
*
|
||||
* @module tiny_mediacms/imagemodal
|
||||
* @copyright 2022 Huong Nguyen <huongnv13@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
import Modal from 'core/modal';
|
||||
import {component} from './common';
|
||||
|
||||
export default class ImageModal extends Modal {
|
||||
static TYPE = `${component}/imagemodal`;
|
||||
static TEMPLATE = `${component}/insert_image_modal`;
|
||||
|
||||
registerEventListeners() {
|
||||
// Call the parent registration.
|
||||
super.registerEventListeners();
|
||||
|
||||
// Register to close on save/cancel.
|
||||
this.registerCloseOnSave();
|
||||
this.registerCloseOnCancel();
|
||||
}
|
||||
|
||||
configure(modalConfig) {
|
||||
modalConfig.large = true;
|
||||
modalConfig.removeOnClose = true;
|
||||
modalConfig.show = true;
|
||||
|
||||
super.configure(modalConfig);
|
||||
}
|
||||
}
|
||||
|
||||
ImageModal.registerModalType();
|
||||
86
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/manager.js
Executable file
86
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/manager.js
Executable file
@@ -0,0 +1,86 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Tiny Media Manager plugin class for Moodle.
|
||||
*
|
||||
* @module tiny_mediacms/manager
|
||||
* @copyright 2022, Stevani Andolo <stevani@hotmail.com.au>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
import Templates from 'core/templates';
|
||||
import {getString} from 'core/str';
|
||||
import Modal from 'core/modal';
|
||||
import * as ModalEvents from 'core/modal_events';
|
||||
import {getData} from './options';
|
||||
import Config from 'core/config';
|
||||
|
||||
export default class MediaManager {
|
||||
|
||||
editor = null;
|
||||
area = null;
|
||||
|
||||
constructor(editor) {
|
||||
this.editor = editor;
|
||||
const data = getData(editor);
|
||||
this.area = data.params.area;
|
||||
this.area.itemid = data.fpoptions.image.itemid;
|
||||
}
|
||||
|
||||
async displayDialogue() {
|
||||
const modal = await Modal.create({
|
||||
large: true,
|
||||
title: getString('mediamanagerproperties', 'tiny_mediacms'),
|
||||
body: Templates.render('tiny_mediacms/mm2_iframe', {
|
||||
src: this.getIframeURL()
|
||||
}),
|
||||
removeOnClose: true,
|
||||
show: true,
|
||||
});
|
||||
modal.getRoot().on(ModalEvents.bodyRendered, () => {
|
||||
this.selectFirstElement();
|
||||
});
|
||||
|
||||
document.querySelector('.modal-lg').style.cssText = `max-width: 850px`;
|
||||
return modal;
|
||||
}
|
||||
|
||||
// It will select the first element in the file manager.
|
||||
selectFirstElement() {
|
||||
const iframe = document.getElementById('mm2-iframe');
|
||||
iframe.addEventListener('load', function() {
|
||||
let intervalId = setInterval(function() {
|
||||
const iDocument = iframe.contentWindow.document;
|
||||
if (iDocument.querySelector('.filemanager')) {
|
||||
const firstFocusableElement = iDocument.querySelector('.fp-navbar a:not([disabled])');
|
||||
if (firstFocusableElement) {
|
||||
firstFocusableElement.focus();
|
||||
}
|
||||
clearInterval(intervalId);
|
||||
}
|
||||
}, 200);
|
||||
});
|
||||
}
|
||||
|
||||
getIframeURL() {
|
||||
const url = new URL(`${Config.wwwroot}/lib/editor/tiny/plugins/mediacms/manage.php`);
|
||||
url.searchParams.append('elementid', this.editor.getElement().id);
|
||||
for (const key in this.area) {
|
||||
url.searchParams.append(key, this.area[key]);
|
||||
}
|
||||
return url.toString();
|
||||
}
|
||||
}
|
||||
117
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/options.js
Executable file
117
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/options.js
Executable file
@@ -0,0 +1,117 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Options helper for Tiny Media plugin.
|
||||
*
|
||||
* @module tiny_mediacms/options
|
||||
* @copyright 2022 Huong Nguyen <huongnv13@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
import {getPluginOptionName} from 'editor_tiny/options';
|
||||
import {pluginName} from './common';
|
||||
|
||||
const dataName = getPluginOptionName(pluginName, 'data');
|
||||
const permissionsName = getPluginOptionName(pluginName, 'permissions');
|
||||
const ltiName = getPluginOptionName(pluginName, 'lti');
|
||||
|
||||
/**
|
||||
* Register the options for the Tiny Media plugin.
|
||||
*
|
||||
* @param {TinyMCE} editor
|
||||
*/
|
||||
export const register = (editor) => {
|
||||
const registerOption = editor.options.register;
|
||||
|
||||
registerOption(permissionsName, {
|
||||
processor: 'object',
|
||||
"default": {
|
||||
image: {
|
||||
filepicker: false,
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
registerOption(dataName, {
|
||||
processor: 'object',
|
||||
"default": {
|
||||
// MediaCMS video library configuration
|
||||
mediacmsApiUrl: '', // e.g., 'https://deic.mediacms.io/api/v1/media'
|
||||
mediacmsBaseUrl: '', // e.g., 'https://deic.mediacms.io'
|
||||
mediacmsPageSize: 12,
|
||||
// Auto-conversion settings
|
||||
autoConvertEnabled: true, // Enable/disable auto-conversion of pasted MediaCMS URLs
|
||||
autoConvertBaseUrl: '', // Base URL to restrict auto-conversion (empty = allow all MediaCMS domains)
|
||||
autoConvertOptions: {
|
||||
// Default embed options for auto-converted videos
|
||||
showTitle: true,
|
||||
linkTitle: true,
|
||||
showRelated: true,
|
||||
showUserAvatar: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
registerOption(ltiName, {
|
||||
processor: 'object',
|
||||
"default": {
|
||||
// LTI configuration for MediaCMS iframe library
|
||||
toolId: 0, // LTI external tool ID
|
||||
courseId: 0, // Current course ID
|
||||
contentItemUrl: '', // URL to /mod/lti/contentitem.php for Deep Linking
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the permissions configuration for the Tiny Media plugin.
|
||||
*
|
||||
* @param {TinyMCE} editor
|
||||
* @returns {object}
|
||||
*/
|
||||
export const getPermissions = (editor) => editor.options.get(permissionsName);
|
||||
|
||||
/**
|
||||
* Get the permissions configuration for the Tiny Media plugin.
|
||||
*
|
||||
* @param {TinyMCE} editor
|
||||
* @returns {object}
|
||||
*/
|
||||
export const getImagePermissions = (editor) => getPermissions(editor).image;
|
||||
|
||||
/**
|
||||
* Get the permissions configuration for the Tiny Media plugin.
|
||||
*
|
||||
* @param {TinyMCE} editor
|
||||
* @returns {object}
|
||||
*/
|
||||
export const getEmbedPermissions = (editor) => getPermissions(editor).embed;
|
||||
|
||||
/**
|
||||
* Get the data configuration for the Media Manager.
|
||||
*
|
||||
* @param {TinyMCE} editor
|
||||
* @returns {object}
|
||||
*/
|
||||
export const getData = (editor) => editor.options.get(dataName);
|
||||
|
||||
/**
|
||||
* Get the LTI configuration for the MediaCMS iframe library.
|
||||
*
|
||||
* @param {TinyMCE} editor
|
||||
* @returns {object}
|
||||
*/
|
||||
export const getLti = (editor) => editor.options.get(ltiName);
|
||||
184
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/plugin.js
Executable file
184
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/plugin.js
Executable file
@@ -0,0 +1,184 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Tiny Media plugin for Moodle.
|
||||
*
|
||||
* @module tiny_mediacms/plugin
|
||||
* @copyright 2022 Andrew Lyons <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
import {getTinyMCE} from 'editor_tiny/loader';
|
||||
import {getPluginMetadata} from 'editor_tiny/utils';
|
||||
|
||||
import {component, pluginName} from './common';
|
||||
import * as Commands from './commands';
|
||||
import * as Configuration from './configuration';
|
||||
import * as Options from './options';
|
||||
import {setupAutoConvert} from './autoconvert';
|
||||
|
||||
/**
|
||||
* Check if a URL is a MediaCMS URL (embed or view).
|
||||
*
|
||||
* @param {string} url - The URL to check
|
||||
* @returns {boolean} True if it's a MediaCMS URL
|
||||
*/
|
||||
const isMediaCMSUrl = (url) => {
|
||||
if (!url) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
const urlObj = new URL(url);
|
||||
// Match both /embed and /view paths with ?m= parameter
|
||||
return (urlObj.pathname === '/embed' || urlObj.pathname === '/view') && urlObj.searchParams.has('m');
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert a MediaCMS URL (embed or view) to an iframe HTML string.
|
||||
* If it's a view URL, it will be converted to embed URL.
|
||||
*
|
||||
* @param {string} url - The MediaCMS URL
|
||||
* @returns {string} The iframe HTML
|
||||
*/
|
||||
const mediaCMSUrlToIframe = (url) => {
|
||||
// Convert view URL to embed URL if needed
|
||||
let embedUrl = url;
|
||||
try {
|
||||
const urlObj = new URL(url);
|
||||
if (urlObj.pathname === '/view') {
|
||||
urlObj.pathname = '/embed';
|
||||
embedUrl = urlObj.toString();
|
||||
}
|
||||
} catch (e) {
|
||||
// Keep original URL if parsing fails
|
||||
}
|
||||
|
||||
return `<iframe src="${embedUrl}" ` +
|
||||
`style="width: 100%; aspect-ratio: 16 / 9; display: block; border: 0;" ` +
|
||||
`allowfullscreen="allowfullscreen"></iframe>`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Regular expression to match standalone MediaCMS URLs in content.
|
||||
* Matches URLs that are on their own line or surrounded by whitespace/tags.
|
||||
* The URL must contain /embed?m= or /view?m= pattern.
|
||||
*/
|
||||
const MEDIACMS_URL_PATTERN = /(^|>|\s)(https?:\/\/[^\s<>"]+\/(?:embed|view)\?m=[^\s<>"]+)(<|\s|$)/g;
|
||||
|
||||
// eslint-disable-next-line no-async-promise-executor
|
||||
export default new Promise(async(resolve) => {
|
||||
const [
|
||||
tinyMCE,
|
||||
setupCommands,
|
||||
pluginMetadata,
|
||||
] = await Promise.all([
|
||||
getTinyMCE(),
|
||||
Commands.getSetup(),
|
||||
getPluginMetadata(component, pluginName),
|
||||
]);
|
||||
|
||||
tinyMCE.PluginManager.add(`${component}/plugin`, (editor) => {
|
||||
// Register options.
|
||||
Options.register(editor);
|
||||
|
||||
// Setup the Commands (buttons, menu items, and so on).
|
||||
setupCommands(editor);
|
||||
|
||||
// Setup auto-conversion of pasted MediaCMS URLs.
|
||||
setupAutoConvert(editor);
|
||||
|
||||
// Convert MediaCMS URLs to iframes when content is loaded into the editor.
|
||||
// This handles content from the database that was saved as just URLs.
|
||||
editor.on('BeforeSetContent', (e) => {
|
||||
if (e.content && typeof e.content === 'string') {
|
||||
// Replace standalone MediaCMS URLs with iframes
|
||||
e.content = e.content.replace(MEDIACMS_URL_PATTERN, (match, before, url, after) => {
|
||||
// Verify it's a valid MediaCMS URL
|
||||
if (isMediaCMSUrl(url)) {
|
||||
return before + mediaCMSUrlToIframe(url) + after;
|
||||
}
|
||||
return match;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Convert MediaCMS iframes back to just embed URLs when saving.
|
||||
// This stores only the URL in the database, not the full iframe HTML.
|
||||
editor.on('GetContent', (e) => {
|
||||
if (e.format === 'html') {
|
||||
// Create a temporary container to manipulate the HTML
|
||||
const tempDiv = document.createElement('div');
|
||||
tempDiv.innerHTML = e.content;
|
||||
|
||||
// Remove edit buttons
|
||||
tempDiv.querySelectorAll('.tiny-mediacms-edit-btn').forEach(btn => btn.remove());
|
||||
|
||||
// Process all iframes - convert MediaCMS iframes to just URLs
|
||||
tempDiv.querySelectorAll('iframe').forEach(iframe => {
|
||||
const src = iframe.getAttribute('src');
|
||||
if (isMediaCMSUrl(src)) {
|
||||
// Check if iframe is inside a wrapper
|
||||
const wrapper = iframe.closest('.tiny-mediacms-iframe-wrapper') ||
|
||||
iframe.closest('.tiny-iframe-responsive');
|
||||
|
||||
// Create a text node with just the URL
|
||||
const urlText = document.createTextNode(src);
|
||||
|
||||
// Wrap in a paragraph for proper formatting
|
||||
const p = document.createElement('p');
|
||||
p.appendChild(urlText);
|
||||
|
||||
if (wrapper) {
|
||||
// Replace the entire wrapper with the URL
|
||||
wrapper.parentNode.insertBefore(p, wrapper);
|
||||
wrapper.remove();
|
||||
} else {
|
||||
// Replace just the iframe with the URL
|
||||
iframe.parentNode.insertBefore(p, iframe);
|
||||
iframe.remove();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Clean up any remaining wrappers that might not have had MediaCMS iframes
|
||||
tempDiv.querySelectorAll('.tiny-mediacms-iframe-wrapper').forEach(wrapper => {
|
||||
const iframe = wrapper.querySelector('iframe');
|
||||
if (iframe) {
|
||||
wrapper.parentNode.insertBefore(iframe, wrapper);
|
||||
}
|
||||
wrapper.remove();
|
||||
});
|
||||
|
||||
tempDiv.querySelectorAll('.tiny-iframe-responsive').forEach(wrapper => {
|
||||
const iframe = wrapper.querySelector('iframe');
|
||||
if (iframe) {
|
||||
wrapper.parentNode.insertBefore(iframe, wrapper);
|
||||
}
|
||||
wrapper.remove();
|
||||
});
|
||||
|
||||
e.content = tempDiv.innerHTML;
|
||||
}
|
||||
});
|
||||
|
||||
return pluginMetadata;
|
||||
});
|
||||
|
||||
// Resolve the Media Plugin and include configuration.
|
||||
resolve([`${component}/plugin`, Configuration]);
|
||||
});
|
||||
162
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/selectors.js
Executable file
162
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/selectors.js
Executable file
@@ -0,0 +1,162 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Tiny Media plugin helper function to build queryable data selectors.
|
||||
*
|
||||
* @module tiny_mediacms/selectors
|
||||
* @copyright 2022 Huong Nguyen <huongnv13@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
export default {
|
||||
IMAGE: {
|
||||
actions: {
|
||||
submit: '.tiny_imagecms_urlentrysubmit',
|
||||
imageBrowser: '.openimagecmsbrowser',
|
||||
addUrl: '.tiny_imagecms_addurl',
|
||||
deleteImage: '.tiny_imagecms_deleteicon',
|
||||
},
|
||||
elements: {
|
||||
form: 'form.tiny_imagecms_form',
|
||||
alignSettings: '.tiny_imagecms_button',
|
||||
alt: '.tiny_imagecms_altentry',
|
||||
altWarning: '.tiny_imagecms_altwarning',
|
||||
height: '.tiny_imagecms_heightentry',
|
||||
width: '.tiny_imagecms_widthentry',
|
||||
url: '.tiny_imagecms_urlentry',
|
||||
urlWarning: '.tiny_imagecms_urlwarning',
|
||||
size: '.tiny_imagecms_size',
|
||||
presentation: '.tiny_imagecms_presentation',
|
||||
constrain: '.tiny_imagecms_constrain',
|
||||
customStyle: '.tiny_imagecms_customstyle',
|
||||
preview: '.tiny_imagecms_preview',
|
||||
previewBox: '.tiny_imagecms_preview_box',
|
||||
loaderIcon: '.tiny_imagecms_loader',
|
||||
loaderIconContainer: '.tiny_imagecms_loader_container',
|
||||
insertImage: '.tiny_imagecms_insert_image',
|
||||
modalFooter: '.modal-footer',
|
||||
dropzoneContainer: '.tiny_imagecms_dropzone_container',
|
||||
fileInput: '#tiny_imagecms_fileinput',
|
||||
fileNameLabel: '.tiny_imagecms_filename',
|
||||
sizeOriginal: '.tiny_imagecms_sizeoriginal',
|
||||
sizeCustom: '.tiny_imagecms_sizecustom',
|
||||
properties: '.tiny_imagecms_properties',
|
||||
},
|
||||
styles: {
|
||||
responsive: 'img-fluid',
|
||||
},
|
||||
},
|
||||
EMBED: {
|
||||
actions: {
|
||||
submit: '.tiny_mediacms_submit',
|
||||
mediaBrowser: '.openmediacmsbrowser',
|
||||
},
|
||||
elements: {
|
||||
form: 'form.tiny_mediacms_form',
|
||||
source: '.tiny_mediacms_source',
|
||||
track: '.tiny_mediacms_track',
|
||||
mediaSource: '.tiny_mediacms_media_source',
|
||||
linkSource: '.tiny_mediacms_link_source',
|
||||
linkSize: '.tiny_mediacms_link_size',
|
||||
posterSource: '.tiny_mediacms_poster_source',
|
||||
posterSize: '.tiny_mediacms_poster_size',
|
||||
displayOptions: '.tiny_mediacms_display_options',
|
||||
name: '.tiny_mediacms_name_entry',
|
||||
title: '.tiny_mediacms_title_entry',
|
||||
url: '.tiny_mediacms_url_entry',
|
||||
width: '.tiny_mediacms_width_entry',
|
||||
height: '.tiny_mediacms_height_entry',
|
||||
trackSource: '.tiny_mediacms_track_source',
|
||||
trackKind: '.tiny_mediacms_track_kind_entry',
|
||||
trackLabel: '.tiny_mediacms_track_label_entry',
|
||||
trackLang: '.tiny_mediacms_track_lang_entry',
|
||||
trackDefault: '.tiny_mediacms_track_default',
|
||||
mediaControl: '.tiny_mediacms_controls',
|
||||
mediaAutoplay: '.tiny_mediacms_autoplay',
|
||||
mediaMute: '.tiny_mediacms_mute',
|
||||
mediaLoop: '.tiny_mediacms_loop',
|
||||
advancedSettings: '.tiny_mediacms_advancedsettings',
|
||||
linkTab: 'li[data-medium-type="link"]',
|
||||
videoTab: 'li[data-medium-type="video"]',
|
||||
audioTab: 'li[data-medium-type="audio"]',
|
||||
linkPane: '.tab-pane[data-medium-type="link"]',
|
||||
videoPane: '.tab-pane[data-medium-type="video"]',
|
||||
audioPane: '.tab-pane[data-medium-type="audio"]',
|
||||
trackSubtitlesTab: 'li[data-track-kind="subtitles"]',
|
||||
trackCaptionsTab: 'li[data-track-kind="captions"]',
|
||||
trackDescriptionsTab: 'li[data-track-kind="descriptions"]',
|
||||
trackChaptersTab: 'li[data-track-kind="chapters"]',
|
||||
trackMetadataTab: 'li[data-track-kind="metadata"]',
|
||||
trackSubtitlesPane: '.tab-pane[data-track-kind="subtitles"]',
|
||||
trackCaptionsPane: '.tab-pane[data-track-kind="captions"]',
|
||||
trackDescriptionsPane: '.tab-pane[data-track-kind="descriptions"]',
|
||||
trackChaptersPane: '.tab-pane[data-track-kind="chapters"]',
|
||||
trackMetadataPane: '.tab-pane[data-track-kind="metadata"]',
|
||||
},
|
||||
mediaTypes: {
|
||||
link: 'LINK',
|
||||
video: 'VIDEO',
|
||||
audio: 'AUDIO',
|
||||
},
|
||||
trackKinds: {
|
||||
subtitles: 'SUBTITLES',
|
||||
captions: 'CAPTIONS',
|
||||
descriptions: 'DESCRIPTIONS',
|
||||
chapters: 'CHAPTERS',
|
||||
metadata: 'METADATA',
|
||||
},
|
||||
},
|
||||
IFRAME: {
|
||||
actions: {
|
||||
remove: '[data-action="remove"]',
|
||||
},
|
||||
elements: {
|
||||
form: 'form.tiny_iframecms_form',
|
||||
url: '.tiny_iframecms_url',
|
||||
urlWarning: '.tiny_iframecms_url_warning',
|
||||
showTitle: '.tiny_iframecms_showtitle',
|
||||
linkTitle: '.tiny_iframecms_linktitle',
|
||||
showRelated: '.tiny_iframecms_showrelated',
|
||||
showUserAvatar: '.tiny_iframecms_showuseravatar',
|
||||
responsive: '.tiny_iframecms_responsive',
|
||||
startAt: '.tiny_iframecms_startat',
|
||||
startAtEnabled: '.tiny_iframecms_startat_enabled',
|
||||
aspectRatio: '.tiny_iframecms_aspectratio',
|
||||
width: '.tiny_iframecms_width',
|
||||
height: '.tiny_iframecms_height',
|
||||
preview: '.tiny_iframecms_preview',
|
||||
previewContainer: '.tiny_iframecms_preview_container',
|
||||
// Tab elements
|
||||
tabs: '.tiny_iframecms_tabs',
|
||||
tabUrlBtn: '.tiny_iframecms_tab_url_btn',
|
||||
tabIframeLibraryBtn: '.tiny_iframecms_tab_iframe_library_btn',
|
||||
paneUrl: '.tiny_iframecms_pane_url',
|
||||
paneIframeLibrary: '.tiny_iframecms_pane_iframe_library',
|
||||
// Iframe library elements
|
||||
iframeLibraryContainer: '.tiny_iframecms_iframe_library_container',
|
||||
iframeLibraryPlaceholder:
|
||||
'.tiny_iframecms_iframe_library_placeholder',
|
||||
iframeLibraryLoading: '.tiny_iframecms_iframe_library_loading',
|
||||
iframeLibraryFrame: '.tiny_iframecms_iframe_library_frame',
|
||||
},
|
||||
aspectRatios: {
|
||||
'16:9': { width: 560, height: 315 },
|
||||
'4:3': { width: 560, height: 420 },
|
||||
'1:1': { width: 400, height: 400 },
|
||||
custom: null,
|
||||
},
|
||||
},
|
||||
};
|
||||
95
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/usedfiles.js
Executable file
95
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/usedfiles.js
Executable file
@@ -0,0 +1,95 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Tiny Media Manager usedfiles.
|
||||
*
|
||||
* @module tiny_mediacms/usedfiles
|
||||
* @copyright 2022, Stevani Andolo <stevani@hotmail.com.au>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
import * as Templates from 'core/templates';
|
||||
import Config from 'core/config';
|
||||
|
||||
class UsedFileManager {
|
||||
constructor(files, userContext, itemId, elementId) {
|
||||
this.files = files;
|
||||
this.userContext = userContext;
|
||||
this.itemId = itemId;
|
||||
this.elementId = elementId;
|
||||
}
|
||||
|
||||
getElementId() {
|
||||
return this.elementId;
|
||||
}
|
||||
|
||||
getUsedFiles() {
|
||||
const editor = window.parent.tinymce.EditorManager.get(this.getElementId());
|
||||
if (!editor) {
|
||||
window.console.error(`Editor not found for ${this.getElementId()}`);
|
||||
return [];
|
||||
}
|
||||
const content = editor.getContent();
|
||||
const baseUrl = `${Config.wwwroot}/draftfile.php/${this.userContext}/user/draft/${this.itemId}/`;
|
||||
const pattern = new RegExp("[\"']" + baseUrl.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&') + "(?<filename>.+?)[\\?\"']", 'gm');
|
||||
|
||||
const usedFiles = [...content.matchAll(pattern)].map((match) => decodeURIComponent(match.groups.filename));
|
||||
|
||||
return usedFiles;
|
||||
}
|
||||
|
||||
// Return an array of unused files.
|
||||
findUnusedFiles(usedFiles) {
|
||||
return Object.entries(this.files)
|
||||
.filter(([filename]) => !usedFiles.includes(filename))
|
||||
.map(([filename]) => filename);
|
||||
}
|
||||
|
||||
// Return an array of missing files.
|
||||
findMissingFiles(usedFiles) {
|
||||
return usedFiles.filter((filename) => !this.files.hasOwnProperty(filename));
|
||||
}
|
||||
|
||||
updateFiles() {
|
||||
const form = document.querySelector('form');
|
||||
const usedFiles = this.getUsedFiles();
|
||||
const unusedFiles = this.findUnusedFiles(usedFiles);
|
||||
const missingFiles = this.findMissingFiles(usedFiles);
|
||||
|
||||
form.querySelectorAll('input[type=checkbox][name^="deletefile"]').forEach((checkbox) => {
|
||||
if (!unusedFiles.includes(checkbox.dataset.filename)) {
|
||||
checkbox.closest('.fitem').remove();
|
||||
}
|
||||
});
|
||||
|
||||
form.classList.toggle('has-missing-files', !!missingFiles.length);
|
||||
form.classList.toggle('has-unused-files', !!unusedFiles.length);
|
||||
|
||||
return Templates.renderForPromise('tiny_mediacms/missingfiles', {
|
||||
missingFiles,
|
||||
}).then(({html, js}) => {
|
||||
Templates.replaceNodeContents(form.querySelector('.missing-files'), html, js);
|
||||
return;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const init = (files, usercontext, itemid, elementid) => {
|
||||
const manager = new UsedFileManager(files, usercontext, itemid, elementid);
|
||||
manager.updateFiles();
|
||||
|
||||
return manager;
|
||||
};
|
||||
118
lms-plugins/mediacms-moodle/tiny/mediacms/classes/form/manage_files_form.php
Executable file
118
lms-plugins/mediacms-moodle/tiny/mediacms/classes/form/manage_files_form.php
Executable file
@@ -0,0 +1,118 @@
|
||||
<?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 manage files plugin form.
|
||||
*
|
||||
* @package tiny_mediacms
|
||||
* @copyright 2022, Stevani Andolo <stevani@hotmail.com.au>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tiny_mediacms\form;
|
||||
|
||||
use html_writer;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once("{$CFG->libdir}/formslib.php");
|
||||
|
||||
/**
|
||||
* Form allowing to edit files in one draft area.
|
||||
*
|
||||
* @package tiny_mediacms
|
||||
* @copyright 2022, Stevani Andolo <stevani@hotmail.com.au>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class manage_files_form extends \moodleform {
|
||||
|
||||
public function definition() {
|
||||
global $PAGE, $USER;
|
||||
$mform = $this->_form;
|
||||
|
||||
$mform->setDisableShortforms(true);
|
||||
|
||||
$itemid = $this->_customdata['draftitemid'];
|
||||
$elementid = $this->_customdata['elementid'];
|
||||
$options = $this->_customdata['options'];
|
||||
$files = $this->_customdata['files'];
|
||||
$usercontext = $this->_customdata['context'];
|
||||
$removeorphaneddrafts = $this->_customdata['removeorphaneddrafts'] ?? false;
|
||||
|
||||
$mform->addElement('header', 'filemanagerhdr', get_string('filemanager', 'tiny_mediacms'));
|
||||
|
||||
$mform->addElement('hidden', 'itemid');
|
||||
$mform->setType('itemid', PARAM_INT);
|
||||
|
||||
$mform->addElement('hidden', 'maxbytes');
|
||||
$mform->setType('maxbytes', PARAM_INT);
|
||||
|
||||
$mform->addElement('hidden', 'subdirs');
|
||||
$mform->setType('subdirs', PARAM_INT);
|
||||
|
||||
$mform->addElement('hidden', 'accepted_types');
|
||||
$mform->setType('accepted_types', PARAM_RAW);
|
||||
|
||||
$mform->addElement('hidden', 'return_types');
|
||||
$mform->setType('return_types', PARAM_INT);
|
||||
|
||||
$mform->addElement('hidden', 'context');
|
||||
$mform->setType('context', PARAM_INT);
|
||||
|
||||
$mform->addElement('hidden', 'areamaxbytes');
|
||||
$mform->setType('areamaxbytes', PARAM_INT);
|
||||
|
||||
$mform->addElement('hidden', 'elementid');
|
||||
$mform->setType('elementid', PARAM_TEXT);
|
||||
|
||||
$mform->addElement('filemanager', 'files_filemanager', '', null, $options);
|
||||
|
||||
// Let the user know that any drafts not referenced in the text will be removed automatically.
|
||||
if ($removeorphaneddrafts) {
|
||||
$mform->addElement('static', '', '', html_writer::tag(
|
||||
'div',
|
||||
get_string('unusedfilesremovalnotice', 'tiny_mediacms')
|
||||
));
|
||||
}
|
||||
|
||||
$mform->addElement('header', 'missingfileshdr', get_string('missingfiles', 'tiny_mediacms'));
|
||||
$mform->addElement('static', '', '',
|
||||
html_writer::tag(
|
||||
'div',
|
||||
html_writer::tag('div', get_string('hasmissingfiles', 'tiny_mediacms')) .
|
||||
html_writer::tag('div', '', ['class' => 'missing-files']
|
||||
),
|
||||
['class' => 'file-status'])
|
||||
);
|
||||
|
||||
$mform->addElement('header', 'deletefileshdr', get_string('unusedfilesheader', 'tiny_mediacms'));
|
||||
$mform->addElement('static', '', '', html_writer::tag('div', get_string('unusedfilesdesc', 'tiny_mediacms')));
|
||||
|
||||
foreach ($files as $hash => $file) {
|
||||
$mform->addElement('checkbox', "deletefile[{$hash}]", '', $file, ['data-filename' => $file]);
|
||||
$mform->setType("deletefile[{$hash}]", PARAM_INT);
|
||||
}
|
||||
|
||||
$mform->addElement('submit', 'delete', get_string('deleteselected', 'tiny_mediacms'));
|
||||
|
||||
$PAGE->requires->js_call_amd('tiny_mediacms/usedfiles', 'init', [
|
||||
'files' => array_flip($files),
|
||||
'usercontext' => $usercontext->id,
|
||||
'itemid' => $itemid,
|
||||
'elementid' => $elementid,
|
||||
]);
|
||||
}
|
||||
}
|
||||
226
lms-plugins/mediacms-moodle/tiny/mediacms/classes/plugininfo.php
Executable file
226
lms-plugins/mediacms-moodle/tiny/mediacms/classes/plugininfo.php
Executable file
@@ -0,0 +1,226 @@
|
||||
<?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/>.
|
||||
|
||||
namespace tiny_mediacms;
|
||||
|
||||
use context;
|
||||
use context_course;
|
||||
use context_module;
|
||||
use editor_tiny\editor;
|
||||
use editor_tiny\plugin;
|
||||
use editor_tiny\plugin_with_buttons;
|
||||
use editor_tiny\plugin_with_configuration;
|
||||
use editor_tiny\plugin_with_menuitems;
|
||||
use moodle_url;
|
||||
|
||||
/**
|
||||
* Tiny media plugin.
|
||||
*
|
||||
* @package tiny_media
|
||||
* @copyright 2022 Andrew Lyons <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class plugininfo extends plugin implements plugin_with_buttons, plugin_with_menuitems, plugin_with_configuration {
|
||||
/**
|
||||
* Whether the plugin is enabled
|
||||
*
|
||||
* @param context $context The context that the editor is used within
|
||||
* @param array $options The options passed in when requesting the editor
|
||||
* @param array $fpoptions The filepicker options passed in when requesting the editor
|
||||
* @param editor $editor The editor instance in which the plugin is initialised
|
||||
* @return boolean
|
||||
*/
|
||||
public static function is_enabled(
|
||||
context $context,
|
||||
array $options,
|
||||
array $fpoptions,
|
||||
?editor $editor = null
|
||||
): bool {
|
||||
// Disabled if:
|
||||
// - Not logged in or guest.
|
||||
// - Files are not allowed.
|
||||
// - Only URL are supported.
|
||||
$canhavefiles = !empty($options['maxfiles']);
|
||||
$canhaveexternalfiles = !empty($options['return_types']) && ($options['return_types'] & FILE_EXTERNAL);
|
||||
|
||||
return isloggedin() && !isguestuser() && ($canhavefiles || $canhaveexternalfiles);
|
||||
}
|
||||
|
||||
public static function get_available_buttons(): array {
|
||||
return [
|
||||
'tiny_mediacms/tiny_mediacms_image',
|
||||
'tiny_mediacms/tiny_mediacms_video',
|
||||
'tiny_mediacms/tiny_mediacms_iframe',
|
||||
];
|
||||
}
|
||||
|
||||
public static function get_available_menuitems(): array {
|
||||
return [
|
||||
'tiny_mediacms/tiny_mediacms_image',
|
||||
'tiny_mediacms/tiny_mediacms_video',
|
||||
'tiny_mediacms/tiny_mediacms_iframe',
|
||||
];
|
||||
}
|
||||
|
||||
public static function get_plugin_configuration_for_context(
|
||||
context $context,
|
||||
array $options,
|
||||
array $fpoptions,
|
||||
?editor $editor = null
|
||||
): array {
|
||||
|
||||
// TODO Fetch the actual permissions.
|
||||
$permissions = [
|
||||
'image' => [
|
||||
'filepicker' => true,
|
||||
],
|
||||
'embed' => [
|
||||
'filepicker' => true,
|
||||
]
|
||||
];
|
||||
|
||||
// Get LTI configuration for MediaCMS iframe library.
|
||||
$lticonfig = self::get_lti_configuration($context);
|
||||
|
||||
// Get auto-convert configuration.
|
||||
$autoconvertconfig = self::get_autoconvert_configuration();
|
||||
|
||||
return array_merge([
|
||||
'permissions' => $permissions,
|
||||
], self::get_file_manager_configuration($context, $options, $fpoptions), $lticonfig, $autoconvertconfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the auto-convert configuration for pasted MediaCMS URLs.
|
||||
*
|
||||
* @return array Auto-convert configuration data
|
||||
*/
|
||||
protected static function get_autoconvert_configuration(): array {
|
||||
$baseurl = get_config('tiny_mediacms', 'autoconvert_baseurl');
|
||||
|
||||
// Helper function to get config with default value of true.
|
||||
$getboolconfig = function($name) {
|
||||
$value = get_config('tiny_mediacms', $name);
|
||||
// If the setting hasn't been saved yet (false/empty), default to true.
|
||||
// Only return false if explicitly set to '0'.
|
||||
return $value !== '0' && $value !== 0;
|
||||
};
|
||||
|
||||
return [
|
||||
'data' => [
|
||||
'autoConvertEnabled' => true, // Always enabled
|
||||
'autoConvertBaseUrl' => !empty($baseurl) ? $baseurl : '',
|
||||
'autoConvertOptions' => [
|
||||
'showTitle' => $getboolconfig('autoconvert_showtitle'),
|
||||
'linkTitle' => $getboolconfig('autoconvert_linktitle'),
|
||||
'showRelated' => $getboolconfig('autoconvert_showrelated'),
|
||||
'showUserAvatar' => $getboolconfig('autoconvert_showuseravatar'),
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the LTI configuration for the MediaCMS iframe library.
|
||||
*
|
||||
* @param context $context The context that the editor is used within
|
||||
* @return array LTI configuration data
|
||||
*/
|
||||
protected static function get_lti_configuration(context $context): array {
|
||||
global $COURSE;
|
||||
|
||||
// Get the configured LTI tool ID from plugin settings.
|
||||
$ltitoolid = get_config('tiny_mediacms', 'ltitoolid');
|
||||
|
||||
// Determine the course ID from context.
|
||||
$courseid = 0;
|
||||
if ($context instanceof context_course) {
|
||||
$courseid = $context->instanceid;
|
||||
} else if ($context instanceof context_module) {
|
||||
// Get the course from the module context.
|
||||
$coursecontext = $context->get_course_context(false);
|
||||
if ($coursecontext) {
|
||||
$courseid = $coursecontext->instanceid;
|
||||
}
|
||||
} else if (!empty($COURSE->id) && $COURSE->id != SITEID) {
|
||||
// Fall back to the global $COURSE if available and not the site.
|
||||
$courseid = $COURSE->id;
|
||||
}
|
||||
|
||||
// Build the content item URL for LTI Deep Linking.
|
||||
// This URL initiates the LTI Deep Linking flow which allows users
|
||||
// to select content (like videos) from the tool provider.
|
||||
$contentitemurl = '';
|
||||
if (!empty($ltitoolid) && $courseid > 0) {
|
||||
$contentitemurl = (new moodle_url('/mod/lti/contentitem.php', [
|
||||
'id' => $ltitoolid,
|
||||
'course' => $courseid,
|
||||
'title' => 'MediaCMS Library',
|
||||
'return_types' => 1 // LTI_DEEPLINKING_RETURN_TYPE_LTI_LINK
|
||||
]))->out(false);
|
||||
}
|
||||
|
||||
return [
|
||||
'lti' => [
|
||||
'toolId' => !empty($ltitoolid) ? (int) $ltitoolid : 0,
|
||||
'courseId' => $courseid,
|
||||
'contentItemUrl' => $contentitemurl,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
protected static function get_file_manager_configuration(
|
||||
context $context,
|
||||
array $options,
|
||||
array $fpoptions
|
||||
): array {
|
||||
global $USER;
|
||||
|
||||
$params = [
|
||||
'area' => [],
|
||||
'usercontext' => \context_user::instance($USER->id)->id,
|
||||
];
|
||||
|
||||
$keys = [
|
||||
'itemid',
|
||||
'areamaxbytes',
|
||||
'maxbytes',
|
||||
'subdirs',
|
||||
'return_types',
|
||||
'removeorphaneddrafts',
|
||||
];
|
||||
if (isset($options['context'])) {
|
||||
if (is_object($options['context'])) {
|
||||
$params['area']['context'] = $options['context']->id;
|
||||
} else {
|
||||
$params['area']['context'] = $options['context'];
|
||||
}
|
||||
}
|
||||
foreach ($keys as $key) {
|
||||
if (isset($options[$key])) {
|
||||
$params['area'][$key] = $options[$key];
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'storeinrepo' => true,
|
||||
'data' => [
|
||||
'params' => $params,
|
||||
'fpoptions' => $fpoptions,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
30
lms-plugins/mediacms-moodle/tiny/mediacms/classes/privacy/provider.php
Executable file
30
lms-plugins/mediacms-moodle/tiny/mediacms/classes/privacy/provider.php
Executable file
@@ -0,0 +1,30 @@
|
||||
<?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/>.
|
||||
|
||||
namespace tiny_mediacms\privacy;
|
||||
|
||||
/**
|
||||
* Privacy Subsystem implementation for the media plugin for TinyMCE.
|
||||
*
|
||||
* @package tiny_media
|
||||
* @copyright 2022 Andrew Lyons <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class provider implements \core_privacy\local\metadata\null_provider {
|
||||
public static function get_reason(): string {
|
||||
return 'privacy:metadata';
|
||||
}
|
||||
}
|
||||
7
lms-plugins/mediacms-moodle/tiny/mediacms/lang/en/deprecated.txt
Executable file
7
lms-plugins/mediacms-moodle/tiny/mediacms/lang/en/deprecated.txt
Executable file
@@ -0,0 +1,7 @@
|
||||
alignment,tiny_media
|
||||
alignment_bottom,tiny_media
|
||||
alignment_left,tiny_media
|
||||
alignment_middle,tiny_media
|
||||
alignment_right,tiny_media
|
||||
alignment_top,tiny_media
|
||||
helplinktext,tiny_media
|
||||
199
lms-plugins/mediacms-moodle/tiny/mediacms/lang/en/tiny_mediacms.php
Executable file
199
lms-plugins/mediacms-moodle/tiny/mediacms/lang/en/tiny_mediacms.php
Executable file
@@ -0,0 +1,199 @@
|
||||
<?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 'tiny_mediacms', language 'en'.
|
||||
*
|
||||
* @package tiny_mediacms
|
||||
* @copyright 2022 Andrew Lyons <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
$string['addcaptionstrack'] = 'Add caption track';
|
||||
$string['addchapterstrack'] = 'Add chapter track';
|
||||
$string['adddescriptionstrack'] = 'Add description track';
|
||||
$string['addfilesdrop'] = 'Drag and drop an image to upload, or click to select';
|
||||
$string['addmetadatatrack'] = 'Add metadata track';
|
||||
$string['addsource_help'] = 'You are recommended to provide an alternative media source, as desktop and mobile browsers support different file formats.';
|
||||
$string['addsource'] = 'Add alternative source';
|
||||
$string['addsubtitlestrack'] = 'Add subtitle track';
|
||||
$string['addurl'] = 'Add';
|
||||
$string['advancedsettings'] = 'Advanced settings';
|
||||
$string['audio'] = 'Audio';
|
||||
$string['audiosourcelabel'] = 'Audio source URL';
|
||||
$string['autoplay'] = 'Play automatically';
|
||||
$string['browserepositories'] = 'Browse repositories...';
|
||||
$string['browserepositoriesimage'] = 'Browse repositories';
|
||||
$string['captions_help'] = 'Captions may be used to describe everything happening in the track, including non-verbal sounds such as a phone ringing.';
|
||||
$string['captions'] = 'Captions';
|
||||
$string['captionssourcelabel'] = 'Caption track URL';
|
||||
$string['chapters_help'] = 'Chapter titles may be provided for use in navigating the media resource.';
|
||||
$string['chapters'] = 'Chapters';
|
||||
$string['chapterssourcelabel'] = 'Chapter track URL';
|
||||
$string['constrain'] = 'Keep proportion';
|
||||
$string['controls'] = 'Show controls';
|
||||
$string['createmedia'] = 'Insert favorite media';
|
||||
$string['default'] = 'Default';
|
||||
$string['deleteimage'] = 'Delete image';
|
||||
$string['deleteimagewarning'] = 'Are you sure you want to remove the image?';
|
||||
$string['deleteselected'] = 'Delete selected files';
|
||||
$string['descriptions_help'] = 'Audio descriptions may be used to provide a narration which explains visual details not apparent from the audio alone.';
|
||||
$string['descriptions'] = 'Descriptions';
|
||||
$string['descriptionssourcelabel'] = 'Description track URL';
|
||||
$string['displayoptions'] = 'Display options';
|
||||
$string['enteralt'] = 'How would you describe this image to someone who can\'t see it?';
|
||||
$string['entername'] = 'Name';
|
||||
$string['entersource'] = 'Source URL';
|
||||
$string['entertitle'] = 'Title';
|
||||
$string['enterurl'] = 'Add via URL';
|
||||
$string['enterurlor'] = 'Or add via URL';
|
||||
$string['filemanager'] = 'Favorite file manager';
|
||||
$string['hasmissingfiles'] = 'Warning! The following files that are referenced in the text area appear to be missing:';
|
||||
$string['height'] = 'Height';
|
||||
$string['imagebuttontitle'] = 'Favorite Image';
|
||||
$string['imagedetails'] = 'Image details';
|
||||
$string['imageproperties'] = 'Image properties';
|
||||
$string['imageurlrequired'] = 'An image must have a valid URL.';
|
||||
$string['insertimage'] = 'Insert favorite image';
|
||||
$string['label'] = 'Label';
|
||||
$string['languagesavailable'] = 'Languages available';
|
||||
$string['languagesinstalled'] = 'Languages installed';
|
||||
$string['link'] = 'Link';
|
||||
$string['loading'] = 'Preparing the image';
|
||||
$string['loop'] = 'Loop';
|
||||
$string['managefiles'] = 'Manage favorite files';
|
||||
$string['mediabuttontitle'] = 'Favorite Multimedia';
|
||||
$string['mediamanagerbuttontitle'] = 'Favorite media manager';
|
||||
$string['mediamanagerproperties'] = 'Favorite media manager';
|
||||
$string['metadata_help'] = 'Metadata tracks, for use from a script, may be used only if the player supports metadata.';
|
||||
$string['metadata'] = 'Metadata';
|
||||
$string['metadatasourcelabel'] = 'Metadata track URL';
|
||||
$string['missingfiles'] = 'Missing files';
|
||||
$string['mute'] = 'Muted';
|
||||
$string['pluginname'] = 'MediaCMS';
|
||||
$string['presentation'] = 'This image is decorative only';
|
||||
$string['presentationoraltrequired'] = 'An image must have a description, unless it is marked as decorative only.';
|
||||
$string['privacy:metadata'] = 'The favorite media plugin for TinyMCE does not store any personal data.';
|
||||
$string['remove'] = 'Remove';
|
||||
$string['repositorynotpermitted'] = 'Paste an image link in the field below.';
|
||||
$string['repositoryuploadnotpermitted'] = 'Paste an image link in the field below or<br>click the Browse Repositories button.';
|
||||
$string['saveimage'] = 'Save';
|
||||
$string['size'] = 'Width x height (in pixels)';
|
||||
$string['sizecustom'] = 'Custom size';
|
||||
$string['sizecustom_help'] = 'This image is just a preview. Changes to its size will be visible after you save it.';
|
||||
$string['sizeoriginal'] = 'Original size';
|
||||
$string['srclang'] = 'Language';
|
||||
$string['subtitles_help'] = 'Subtitles may be used to provide a transcription or translation of the dialogue.';
|
||||
$string['subtitles'] = 'Subtitles';
|
||||
$string['subtitlessourcelabel'] = 'Subtitle track URL';
|
||||
$string['tracks_help'] = 'Subtitles, captions, chapters and descriptions can be added via a WebVTT (Web Video Text Tracks) format file. Track labels will be shown in the selection drop-down menu. For each type of track, any track set as default will be pre-selected at the start of the video.';
|
||||
$string['tracks'] = 'Subtitles and captions';
|
||||
$string['unusedfilesdesc'] = 'The following embedded files are not used in the text area:';
|
||||
$string['unusedfilesheader'] = 'Unused files';
|
||||
$string['unusedfilesremovalnotice'] = 'Any unused files will be automatically deleted when saving changes.';
|
||||
$string['updatemedia'] = 'Update favorite media';
|
||||
$string['uploading'] = 'Uploading';
|
||||
$string['video'] = 'Video';
|
||||
$string['videoheight'] = 'Video height';
|
||||
$string['videosourcelabel'] = 'Video source URL';
|
||||
$string['videowidth'] = 'Video width';
|
||||
$string['width'] = 'Width';
|
||||
|
||||
// Iframe embed strings.
|
||||
$string['iframebuttontitle'] = 'Insert MediaCMS Media';
|
||||
$string['iframemodaltitle'] = 'Insert MediaCMS Media';
|
||||
$string['iframeurl'] = 'MediaCMS Video URL or embed code';
|
||||
$string['iframeurlplaceholder'] = 'Paste MediaCMS Video URL or iframe embed code';
|
||||
$string['iframeurlinvalid'] = 'Please enter a valid MediaCMS Video URL or embed code';
|
||||
$string['embedoptions'] = 'Embed Options';
|
||||
$string['showtitle'] = 'Show title';
|
||||
$string['linktitle'] = 'Link title';
|
||||
$string['showrelated'] = 'Show related';
|
||||
$string['showuseravatar'] = 'Show user avatar';
|
||||
$string['responsive'] = 'Responsive';
|
||||
$string['startat'] = 'Start at';
|
||||
$string['aspectratio'] = 'Aspect Ratio';
|
||||
$string['aspectratio_16_9'] = '16:9';
|
||||
$string['aspectratio_4_3'] = '4:3';
|
||||
$string['aspectratio_1_1'] = '1:1';
|
||||
$string['aspectratio_custom'] = 'Custom';
|
||||
$string['dimensions'] = 'Dimensions';
|
||||
$string['preview'] = 'Preview';
|
||||
$string['insertiframe'] = 'Insert video';
|
||||
$string['updateiframe'] = 'Update video';
|
||||
$string['removeiframe'] = 'Remove video';
|
||||
$string['removeiframeconfirm'] = 'Are you sure you want to remove this video from the editor?';
|
||||
|
||||
// Iframe modal tabs.
|
||||
$string['tabembedurl'] = 'Embed URL';
|
||||
$string['tabvideolibrary'] = 'Video Library';
|
||||
$string['tabvideolibraryiframe'] = 'Media Library';
|
||||
|
||||
// Video library strings.
|
||||
$string['librarysearchplaceholder'] = 'Search videos...';
|
||||
$string['librarysortnewest'] = 'Newest first';
|
||||
$string['librarysortoldest'] = 'Oldest first';
|
||||
$string['librarysorttitle'] = 'Title A-Z';
|
||||
$string['librarysortviews'] = 'Most views';
|
||||
$string['libraryloading'] = 'Loading videos...';
|
||||
$string['libraryempty'] = 'No videos found';
|
||||
$string['libraryerror'] = 'Failed to load videos';
|
||||
$string['libraryretry'] = 'Retry';
|
||||
$string['libraryprev'] = 'Previous';
|
||||
$string['librarynext'] = 'Next';
|
||||
$string['libraryselect'] = 'Select';
|
||||
$string['librarypage'] = 'Page {$a->current} of {$a->total}';
|
||||
$string['libraryvideoselected'] = 'Video selected. Configure embed options below.';
|
||||
|
||||
// LTI settings strings.
|
||||
$string['ltitoolid'] = 'LTI Tool';
|
||||
$string['ltitoolid_desc'] = 'Select the External Tool configuration for MediaCMS. This enables the authenticated video library in the editor.';
|
||||
$string['noltitoolsfound'] = 'No LTI tools found';
|
||||
$string['choose'] = 'Choose...';
|
||||
$string['ltitoolid_help'] = 'To find the LTI tool ID, go to Site administration > Plugins > Activity modules > External tool > Manage tools. The ID is shown in the URL when editing a tool (e.g., id=2).';
|
||||
|
||||
// Iframe library from LTI strings.
|
||||
$string['iframelibraryloading'] = 'Loading MediaCMS video library...';
|
||||
$string['iframelibraryplaceholder'] = 'Click here to load the video library';
|
||||
$string['iframelibraryerror'] = 'Failed to load the video library. Please check your LTI configuration.';
|
||||
$string['iframelibrarynotconfigured'] = 'The MediaCMS LTI tool has not been configured. Please contact your administrator.';
|
||||
|
||||
// Auto-convert settings strings.
|
||||
$string['autoconvertheading'] = 'Auto-convert MediaCMS URLs';
|
||||
$string['autoconvertheading_desc'] = 'Configure automatic conversion of pasted MediaCMS URLs to embedded videos.';
|
||||
$string['autoconvertenabled'] = 'Enable auto-convert';
|
||||
$string['autoconvertenabled_desc'] = 'When enabled, pasting a MediaCMS video URL (e.g., https://lti.mediacms.io/view?m=VIDEO_ID) into the editor will automatically convert it to an embedded video player.';
|
||||
$string['autoconvert_baseurl'] = 'MediaCMS URL';
|
||||
$string['autoconvert_baseurl_desc'] = 'The base URL of your MediaCMS instance (e.g., https://lti.mediacms.io). If specified, only URLs from this domain will be auto-converted. Leave empty to allow any MediaCMS URL.';
|
||||
$string['autoconvert_showtitle'] = 'Show video title';
|
||||
$string['autoconvert_showtitle_desc'] = 'Display the video title in the embedded player.';
|
||||
$string['autoconvert_linktitle'] = 'Link video title';
|
||||
$string['autoconvert_linktitle_desc'] = 'Make the video title clickable, linking to the original video page.';
|
||||
$string['autoconvert_showrelated'] = 'Show related videos';
|
||||
$string['autoconvert_showrelated_desc'] = 'Display related videos after the current video ends.';
|
||||
$string['autoconvert_showuseravatar'] = 'Show user avatar';
|
||||
$string['autoconvert_showuseravatar_desc'] = 'Display the uploader\'s avatar in the embedded player.';
|
||||
|
||||
// Deprecated since Moodle 4.4.
|
||||
$string['alignment'] = 'Alignment';
|
||||
$string['alignment_bottom'] = 'Bottom';
|
||||
$string['alignment_left'] = 'Left';
|
||||
$string['alignment_middle'] = 'Middle';
|
||||
$string['alignment_right'] = 'Right';
|
||||
$string['alignment_top'] = 'Top';
|
||||
|
||||
// Deprecated since Moodle 4.5.
|
||||
$string['helplinktext'] = 'Favorite media helper';
|
||||
148
lms-plugins/mediacms-moodle/tiny/mediacms/manage.php
Executable file
148
lms-plugins/mediacms-moodle/tiny/mediacms/manage.php
Executable file
@@ -0,0 +1,148 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Manage files in user draft area attached to texteditor.
|
||||
*
|
||||
* @package tiny_mediacms
|
||||
* @copyright 2022, Stevani Andolo <stevani@hotmail.com.au>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
require(__DIR__ . '/../../../../../config.php');
|
||||
require_once($CFG->libdir . '/filestorage/file_storage.php');
|
||||
require_once($CFG->dirroot . '/repository/lib.php');
|
||||
|
||||
$itemid = required_param('itemid', PARAM_INT) ?? 0;
|
||||
$maxbytes = optional_param('maxbytes', 0, PARAM_INT);
|
||||
$subdirs = optional_param('subdirs', 0, PARAM_INT);
|
||||
$acceptedtypes = optional_param('accepted_types', '*', PARAM_RAW); // TODO Not yet passed to this script.
|
||||
$returntypes = optional_param('return_types', null, PARAM_INT);
|
||||
$areamaxbytes = optional_param('areamaxbytes', FILE_AREA_MAX_BYTES_UNLIMITED, PARAM_INT);
|
||||
$contextid = optional_param('context', SYSCONTEXTID, PARAM_INT);
|
||||
$elementid = optional_param('elementid', '', PARAM_TEXT);
|
||||
$removeorphaneddrafts = optional_param('removeorphaneddrafts', 0, PARAM_INT);
|
||||
|
||||
$context = context::instance_by_id($contextid);
|
||||
if ($context->contextlevel == CONTEXT_MODULE) {
|
||||
// Module context.
|
||||
$cm = $DB->get_record('course_modules', ['id' => $context->instanceid]);
|
||||
require_login($cm->course, true, $cm);
|
||||
} else if (($coursecontext = $context->get_course_context(false)) && $coursecontext->id != SITEID) {
|
||||
// Course context or block inside the course.
|
||||
require_login($coursecontext->instanceid);
|
||||
$PAGE->set_context($context);
|
||||
} else {
|
||||
// Block that is not inside the course, user or system context.
|
||||
require_login();
|
||||
$PAGE->set_context($context);
|
||||
}
|
||||
|
||||
// Guests can never manage files.
|
||||
if (isguestuser()) {
|
||||
throw new \moodle_exception('noguest');
|
||||
}
|
||||
|
||||
$title = get_string('managefiles', 'tiny_mediacms');
|
||||
|
||||
$url = new moodle_url('/lib/editor/tiny/plugins/mediacms/manage.php', [
|
||||
'itemid' => $itemid,
|
||||
'maxbytes' => $maxbytes,
|
||||
'subdirs' => $subdirs,
|
||||
'accepted_types' => $acceptedtypes,
|
||||
'return_types' => $returntypes,
|
||||
'areamaxbytes' => $areamaxbytes,
|
||||
'context' => $contextid,
|
||||
'elementid' => $elementid,
|
||||
'removeorphaneddrafts' => $removeorphaneddrafts,
|
||||
]);
|
||||
|
||||
$PAGE->set_url($url);
|
||||
$PAGE->set_title($title);
|
||||
$PAGE->set_heading($title);
|
||||
$PAGE->set_pagelayout('popup');
|
||||
|
||||
if ($returntypes !== null) {
|
||||
// Links are allowed in textarea but never allowed in filemanager.
|
||||
$returntypes = $returntypes & ~FILE_EXTERNAL;
|
||||
}
|
||||
|
||||
// These are the options required for the filepicker.
|
||||
$options = [
|
||||
'subdirs' => $subdirs,
|
||||
'maxbytes' => $maxbytes,
|
||||
'maxfiles' => -1,
|
||||
'accepted_types' => $acceptedtypes,
|
||||
'areamaxbytes' => $areamaxbytes,
|
||||
'return_types' => $returntypes,
|
||||
'context' => $context
|
||||
];
|
||||
|
||||
$usercontext = context_user::instance($USER->id);
|
||||
$fs = get_file_storage();
|
||||
$files = $fs->get_directory_files($usercontext->id, 'user', 'draft', $itemid, '/', !empty($subdirs), false);
|
||||
$filenames = [];
|
||||
foreach ($files as $file) {
|
||||
$filenames[$file->get_pathnamehash()] = ltrim($file->get_filepath(), '/') . $file->get_filename();
|
||||
}
|
||||
|
||||
$mform = new tiny_mediacms\form\manage_files_form(null, [
|
||||
'context' => $usercontext,
|
||||
'options' => $options,
|
||||
'draftitemid' => $itemid,
|
||||
'files' => $filenames,
|
||||
'elementid' => $elementid,
|
||||
'removeorphaneddrafts' => $removeorphaneddrafts,
|
||||
], 'post', '', [
|
||||
'id' => 'tiny_mediacms_form',
|
||||
]
|
||||
);
|
||||
|
||||
if ($data = $mform->get_data()) {
|
||||
if (!empty($data->deletefile)) {
|
||||
foreach (array_keys($data->deletefile) as $filehash) {
|
||||
if ($file = $fs->get_file_by_hash($filehash)) {
|
||||
// Make sure the user didn't modify the filehash to delete another file.
|
||||
if ($file->get_component() !== 'user' || $file->get_filearea() !== 'draft') {
|
||||
// The file must belong to the user/draft area.
|
||||
continue;
|
||||
}
|
||||
if ($file->get_contextid() != $usercontext->id) {
|
||||
// The user must own the file - that is it must be in their user draft file area.
|
||||
continue;
|
||||
}
|
||||
if ($file->get_itemid() != $itemid) {
|
||||
// It must be the file they requested be deleted.
|
||||
continue;
|
||||
}
|
||||
$file->delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
// Redirect to prevent re-posting the form.
|
||||
redirect($url);
|
||||
}
|
||||
|
||||
$mform->set_data(array_merge($options, [
|
||||
'files_filemanager' => $itemid,
|
||||
'itemid' => $itemid,
|
||||
'elementid' => $elementid,
|
||||
'context' => $context->id,
|
||||
]));
|
||||
|
||||
echo $OUTPUT->header();
|
||||
$mform->display();
|
||||
echo $OUTPUT->footer();
|
||||
206
lms-plugins/mediacms-moodle/tiny/mediacms/mediacms/AUTOCONVERT.md
Executable file
206
lms-plugins/mediacms-moodle/tiny/mediacms/mediacms/AUTOCONVERT.md
Executable file
@@ -0,0 +1,206 @@
|
||||
# MediaCMS URL Auto-Convert Feature
|
||||
|
||||
This feature automatically converts pasted MediaCMS video URLs into embedded video players within the TinyMCE editor.
|
||||
|
||||
## Overview
|
||||
|
||||
When a user pastes a MediaCMS video URL like:
|
||||
```
|
||||
https://deic.mediacms.io/view?m=JpBd1Zvdl
|
||||
```
|
||||
|
||||
It is automatically converted to an embedded video player:
|
||||
```html
|
||||
<div class="tiny-iframe-responsive" contenteditable="false">
|
||||
<iframe
|
||||
style="width: 100%; max-width: calc(100vh * 16 / 9); aspect-ratio: 16 / 9; display: block; margin: auto; border: 0;"
|
||||
src="https://deic.mediacms.io/embed?m=JpBd1Zvdl&showTitle=1&showRelated=1&showUserAvatar=1&linkTitle=1"
|
||||
allowfullscreen="allowfullscreen">
|
||||
</iframe>
|
||||
</div>
|
||||
```
|
||||
|
||||
## Supported URL Formats
|
||||
|
||||
The auto-convert feature recognizes MediaCMS view URLs in this format:
|
||||
- `https://[domain]/view?m=[VIDEO_ID]`
|
||||
|
||||
Examples:
|
||||
- `https://deic.mediacms.io/view?m=JpBd1Zvdl`
|
||||
- `https://your-mediacms-instance.com/view?m=abc123`
|
||||
|
||||
## Configuration
|
||||
|
||||
### Accessing Settings
|
||||
|
||||
1. Log in to Moodle as an administrator
|
||||
2. Navigate to: **Site administration** → **Plugins** → **Text editors** → **TinyMCE editor** → **MediaCMS**
|
||||
3. Scroll to the **Auto-convert MediaCMS URLs** section
|
||||
|
||||
### Available Settings
|
||||
|
||||
| Setting | Description | Default |
|
||||
|---------|-------------|---------|
|
||||
| **Enable auto-convert** | Turn the auto-convert feature on or off | Enabled |
|
||||
| **MediaCMS base URL** | Restrict auto-conversion to a specific MediaCMS domain | Empty (allow all) |
|
||||
| **Show video title** | Display the video title in the embedded player | Enabled |
|
||||
| **Link video title** | Make the video title clickable, linking to the original video page | Enabled |
|
||||
| **Show related videos** | Display related videos after the current video ends | Enabled |
|
||||
| **Show user avatar** | Display the uploader's avatar in the embedded player | Enabled |
|
||||
|
||||
### Settings Location in Moodle
|
||||
|
||||
The settings are stored in the Moodle database under the `tiny_mediacms` plugin configuration:
|
||||
|
||||
- `tiny_mediacms/autoconvertenabled` - Enable/disable auto-convert
|
||||
- `tiny_mediacms/autoconvert_baseurl` - MediaCMS base URL (e.g., https://deic.mediacms.io)
|
||||
- `tiny_mediacms/autoconvert_showtitle` - Show title option
|
||||
- `tiny_mediacms/autoconvert_linktitle` - Link title option
|
||||
- `tiny_mediacms/autoconvert_showrelated` - Show related option
|
||||
- `tiny_mediacms/autoconvert_showuseravatar` - Show user avatar option
|
||||
|
||||
### Base URL Configuration
|
||||
|
||||
The **MediaCMS base URL** setting controls which MediaCMS instances are recognized for auto-conversion:
|
||||
|
||||
- **Empty (default)**: Any MediaCMS URL will be auto-converted (e.g., URLs from any `*/view?m=*` pattern)
|
||||
- **Specific URL**: Only URLs from the specified domain will be auto-converted
|
||||
|
||||
Example configurations:
|
||||
- `https://deic.mediacms.io` - Only convert URLs from deic.mediacms.io
|
||||
- `https://media.myuniversity.edu` - Only convert URLs from your institution's MediaCMS
|
||||
|
||||
## Technical Details
|
||||
|
||||
### File Structure
|
||||
|
||||
```
|
||||
amd/src/
|
||||
├── autoconvert.js # Main auto-convert module
|
||||
├── plugin.js # Plugin initialization (imports autoconvert)
|
||||
└── options.js # Configuration options definition
|
||||
|
||||
classes/
|
||||
└── plugininfo.php # Passes PHP settings to JavaScript
|
||||
|
||||
settings.php # Admin settings page definition
|
||||
|
||||
lang/en/
|
||||
└── tiny_mediacms.php # Language strings for settings
|
||||
```
|
||||
|
||||
### How It Works
|
||||
|
||||
1. **Paste Detection**: The `autoconvert.js` module listens for `paste` events on the TinyMCE editor
|
||||
2. **URL Validation**: When text is pasted, it checks if it matches the MediaCMS URL pattern
|
||||
3. **HTML Generation**: If valid, it generates the responsive iframe HTML with configured options
|
||||
4. **Content Insertion**: The original URL is replaced with the embedded video
|
||||
|
||||
### JavaScript Configuration
|
||||
|
||||
The settings are passed from PHP to JavaScript via the `plugininfo.php` class:
|
||||
|
||||
```php
|
||||
protected static function get_autoconvert_configuration(): array {
|
||||
$baseurl = get_config('tiny_mediacms', 'autoconvert_baseurl');
|
||||
|
||||
return [
|
||||
'data' => [
|
||||
'autoConvertEnabled' => (bool) get_config('tiny_mediacms', 'autoconvertenabled'),
|
||||
'autoConvertBaseUrl' => !empty($baseurl) ? $baseurl : '',
|
||||
'autoConvertOptions' => [
|
||||
'showTitle' => (bool) get_config('tiny_mediacms', 'autoconvert_showtitle'),
|
||||
'linkTitle' => (bool) get_config('tiny_mediacms', 'autoconvert_linktitle'),
|
||||
'showRelated' => (bool) get_config('tiny_mediacms', 'autoconvert_showrelated'),
|
||||
'showUserAvatar' => (bool) get_config('tiny_mediacms', 'autoconvert_showuseravatar'),
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
### Default Values (in options.js)
|
||||
|
||||
If PHP settings are not configured, the JavaScript uses these defaults:
|
||||
|
||||
```javascript
|
||||
registerOption(dataName, {
|
||||
processor: 'object',
|
||||
"default": {
|
||||
autoConvertEnabled: true,
|
||||
autoConvertBaseUrl: '', // Empty = allow all MediaCMS domains
|
||||
autoConvertOptions: {
|
||||
showTitle: true,
|
||||
linkTitle: true,
|
||||
showRelated: true,
|
||||
showUserAvatar: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Customization
|
||||
|
||||
### Disabling Auto-Convert
|
||||
|
||||
To disable the feature entirely:
|
||||
1. Go to the plugin settings (see "Accessing Settings" above)
|
||||
2. Uncheck **Enable auto-convert**
|
||||
3. Save changes
|
||||
|
||||
### Programmatic Configuration
|
||||
|
||||
You can also set these values directly in the database using Moodle's `set_config()` function:
|
||||
|
||||
```php
|
||||
// Disable auto-convert
|
||||
set_config('autoconvertenabled', 0, 'tiny_mediacms');
|
||||
|
||||
// Set the MediaCMS base URL (restrict to specific domain)
|
||||
set_config('autoconvert_baseurl', 'https://deic.mediacms.io', 'tiny_mediacms');
|
||||
|
||||
// Customize embed options
|
||||
set_config('autoconvert_showtitle', 1, 'tiny_mediacms');
|
||||
set_config('autoconvert_linktitle', 0, 'tiny_mediacms');
|
||||
set_config('autoconvert_showrelated', 0, 'tiny_mediacms');
|
||||
set_config('autoconvert_showuseravatar', 1, 'tiny_mediacms');
|
||||
```
|
||||
|
||||
### CLI Configuration
|
||||
|
||||
Using Moodle CLI:
|
||||
|
||||
```bash
|
||||
# Enable auto-convert
|
||||
php admin/cli/cfg.php --component=tiny_mediacms --name=autoconvertenabled --set=1
|
||||
|
||||
# Set the MediaCMS base URL
|
||||
php admin/cli/cfg.php --component=tiny_mediacms --name=autoconvert_baseurl --set=https://deic.mediacms.io
|
||||
|
||||
# Disable showing related videos
|
||||
php admin/cli/cfg.php --component=tiny_mediacms --name=autoconvert_showrelated --set=0
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Auto-convert not working
|
||||
|
||||
1. **Check if enabled**: Verify the setting is enabled in plugin settings
|
||||
2. **Clear caches**: Purge all caches (Site administration → Development → Purge all caches)
|
||||
3. **Check URL format**: Ensure the URL matches the pattern `https://[domain]/view?m=[VIDEO_ID]`
|
||||
4. **Browser console**: Check for JavaScript errors in the browser developer console
|
||||
|
||||
### Rebuilding JavaScript
|
||||
|
||||
If you modify the source files, rebuild using:
|
||||
|
||||
```bash
|
||||
cd /path/to/moodle
|
||||
npx grunt amd --root=public/lib/editor/tiny/plugins/mediacms
|
||||
```
|
||||
|
||||
Note: Requires Node.js 22.x or compatible version as specified in Moodle's requirements.
|
||||
|
||||
## Version History
|
||||
|
||||
- **1.0.0** - Initial implementation of auto-convert feature
|
||||
15
lms-plugins/mediacms-moodle/tiny/mediacms/mediacms/amd/build/autoconvert.min.js
vendored
Executable file
15
lms-plugins/mediacms-moodle/tiny/mediacms/mediacms/amd/build/autoconvert.min.js
vendored
Executable file
@@ -0,0 +1,15 @@
|
||||
define("tiny_mediacms/autoconvert",["exports","./options"],(function(_exports,_options){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.setupAutoConvert=_exports.isMediaCMSUrl=_exports.convertToEmbed=void 0;
|
||||
/**
|
||||
* Tiny MediaCMS Auto-convert module.
|
||||
*
|
||||
* This module automatically converts pasted MediaCMS URLs into embedded videos.
|
||||
* When a user pastes a MediaCMS video URL (e.g., https://deic.mediacms.io/view?m=JpBd1Zvdl),
|
||||
* it will be automatically converted to an iframe embed.
|
||||
*
|
||||
* @module tiny_mediacms/autoconvert
|
||||
* @copyright 2024
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
const MEDIACMS_VIEW_URL_PATTERN=/^(https?:\/\/[^\/]+)\/view\?m=([a-zA-Z0-9_-]+)$/,parseMediaCMSUrl=text=>{if(!text||"string"!=typeof text)return null;const trimmed=text.trim(),match=trimmed.match(MEDIACMS_VIEW_URL_PATTERN);return match?{baseUrl:match[1],videoId:match[2],originalUrl:trimmed}:null},isDomainAllowed=(parsed,config)=>{const configuredBaseUrl=config.autoConvertBaseUrl||config.mediacmsBaseUrl;if(!configuredBaseUrl)return!0;try{const configuredUrl=new URL(configuredBaseUrl),pastedUrl=new URL(parsed.baseUrl);return configuredUrl.host===pastedUrl.host}catch(e){return!0}},generateEmbedHtml=function(parsed){let options=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const embedUrl=new URL("".concat(parsed.baseUrl,"/embed"));embedUrl.searchParams.set("m",parsed.videoId),embedUrl.searchParams.set("showTitle",!1!==options.showTitle?"1":"0"),embedUrl.searchParams.set("showRelated",!1!==options.showRelated?"1":"0"),embedUrl.searchParams.set("showUserAvatar",!1!==options.showUserAvatar?"1":"0"),embedUrl.searchParams.set("linkTitle",!1!==options.linkTitle?"1":"0");const html='<iframe width="400" height="300" style="display: block; border: 0;" '+'src="'.concat(embedUrl.toString(),'" ')+'allowfullscreen="allowfullscreen"></iframe>';return html};_exports.setupAutoConvert=editor=>{const config=(0,_options.getData)(editor)||{};!1!==config.autoConvertEnabled&&(editor.on("paste",(e=>{handlePasteEvent(editor,e,config)})),editor.on("input",(e=>{handleInputEvent(editor,e,config)})))};const handlePasteEvent=(editor,e,config)=>{const clipboardData=e.clipboardData||window.clipboardData;if(!clipboardData)return;const text=clipboardData.getData("text/plain")||clipboardData.getData("text");if(!text)return;const parsed=parseMediaCMSUrl(text);if(!parsed)return;if(!isDomainAllowed(parsed,config))return;e.preventDefault(),e.stopPropagation();const embedHtml=generateEmbedHtml(parsed,config.autoConvertOptions||{});setTimeout((()=>{editor.insertContent(embedHtml),editor.selection.collapse(!1)}),0)},handleInputEvent=(editor,e,config)=>{if("insertFromPaste"!==e.inputType&&"insertText"!==e.inputType)return;const node=editor.selection.getNode();if(!node||"P"!==node.nodeName)return;const text=node.textContent||"",parsed=parseMediaCMSUrl(text);if(!parsed||!isDomainAllowed(parsed,config))return;const trimmedHtml=node.innerHTML.trim();if(trimmedHtml!==text.trim()&&!trimmedHtml.startsWith(text.trim()))return;const embedHtml=generateEmbedHtml(parsed,config.autoConvertOptions||{});setTimeout((()=>{const currentText=node.textContent||"",currentParsed=parseMediaCMSUrl(currentText);currentParsed&¤tParsed.originalUrl===parsed.originalUrl&&(editor.selection.select(node),editor.insertContent(embedHtml))}),100)};_exports.isMediaCMSUrl=text=>null!==parseMediaCMSUrl(text);_exports.convertToEmbed=function(url){let options=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const parsed=parseMediaCMSUrl(url);return parsed?generateEmbedHtml(parsed,options):null}}));
|
||||
|
||||
//# sourceMappingURL=autoconvert.min.js.map
|
||||
File diff suppressed because one or more lines are too long
10
lms-plugins/mediacms-moodle/tiny/mediacms/mediacms/amd/build/commands.min.js
vendored
Executable file
10
lms-plugins/mediacms-moodle/tiny/mediacms/mediacms/amd/build/commands.min.js
vendored
Executable file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
3
lms-plugins/mediacms-moodle/tiny/mediacms/mediacms/amd/build/common.min.js
vendored
Executable file
3
lms-plugins/mediacms-moodle/tiny/mediacms/mediacms/amd/build/common.min.js
vendored
Executable file
@@ -0,0 +1,3 @@
|
||||
define("tiny_mediacms/common",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;return _exports.default={pluginName:"tiny_mediacms/plugin",component:"tiny_mediacms",iframeButtonName:"tiny_mediacms_iframe",iframeMenuItemName:"tiny_mediacms_iframe",iframeIcon:"tiny_mediacms_iframe"},_exports.default}));
|
||||
|
||||
//# sourceMappingURL=common.min.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"common.min.js","sources":["../src/common.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Tiny Media common values.\n *\n * @module tiny_mediacms/common\n * @copyright 2022 Huong Nguyen <huongnv13@gmail.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nexport default {\n pluginName: 'tiny_mediacms/plugin',\n component: 'tiny_mediacms',\n iframeButtonName: 'tiny_mediacms_iframe',\n iframeMenuItemName: 'tiny_mediacms_iframe',\n iframeIcon: 'tiny_mediacms_iframe',\n};\n"],"names":["pluginName","component","iframeButtonName","iframeMenuItemName","iframeIcon"],"mappings":"sKAuBe,CACXA,WAAY,uBACZC,UAAW,gBACXC,iBAAkB,uBAClBC,mBAAoB,uBACpBC,WAAY"}
|
||||
3
lms-plugins/mediacms-moodle/tiny/mediacms/mediacms/amd/build/configuration.min.js
vendored
Executable file
3
lms-plugins/mediacms-moodle/tiny/mediacms/mediacms/amd/build/configuration.min.js
vendored
Executable file
@@ -0,0 +1,3 @@
|
||||
define("tiny_mediacms/configuration",["exports","./common","editor_tiny/utils"],(function(_exports,_common,_utils){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.configure=void 0;_exports.configure=instanceConfig=>{return{contextmenu:(0,_utils.addContextmenuItem)(instanceConfig.contextmenu,_common.iframeButtonName),menu:(menu=instanceConfig.menu,menu.insert.items="".concat(_common.iframeMenuItemName," ").concat(menu.insert.items),menu),toolbar:(toolbar=instanceConfig.toolbar,toolbar.map((section=>("content"===section.name&§ion.items.unshift(_common.iframeButtonName),section))))};var toolbar,menu}}));
|
||||
|
||||
//# sourceMappingURL=configuration.min.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"configuration.min.js","sources":["../src/configuration.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Tiny Media configuration.\n *\n * @module tiny_mediacms/configuration\n * @copyright 2022 Huong Nguyen <huongnv13@gmail.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {\n iframeButtonName,\n iframeMenuItemName,\n} from './common';\nimport {\n addContextmenuItem,\n} from 'editor_tiny/utils';\n\nconst configureMenu = (menu) => {\n // Add the Iframe Embed to the insert menu.\n menu.insert.items = `${iframeMenuItemName} ${menu.insert.items}`;\n\n return menu;\n};\n\nconst configureToolbar = (toolbar) => {\n // The toolbar contains an array of named sections.\n // The Moodle integration ensures that there is a section called 'content'.\n\n return toolbar.map((section) => {\n if (section.name === 'content') {\n // Insert the iframe button at the start of it.\n section.items.unshift(iframeButtonName);\n }\n\n return section;\n });\n};\n\nexport const configure = (instanceConfig) => {\n // Update the instance configuration to add the Iframe Embed menu option to the menus and toolbars.\n return {\n contextmenu: addContextmenuItem(instanceConfig.contextmenu, iframeButtonName),\n menu: configureMenu(instanceConfig.menu),\n toolbar: configureToolbar(instanceConfig.toolbar),\n };\n};\n"],"names":["instanceConfig","contextmenu","iframeButtonName","menu","insert","items","iframeMenuItemName","toolbar","map","section","name","unshift"],"mappings":"wNAoD0BA,uBAEf,CACHC,aAAa,6BAAmBD,eAAeC,YAAaC,0BAC5DC,MAzBeA,KAyBKH,eAAeG,KAvBvCA,KAAKC,OAAOC,gBAAWC,uCAAsBH,KAAKC,OAAOC,OAElDF,MAsBHI,SAnBkBA,QAmBQP,eAAeO,QAftCA,QAAQC,KAAKC,UACK,YAAjBA,QAAQC,MAERD,QAAQJ,MAAMM,QAAQT,0BAGnBO,aAVWF,IAAAA,QAPHJ"}
|
||||
3
lms-plugins/mediacms-moodle/tiny/mediacms/mediacms/amd/build/embed.min.js
vendored
Executable file
3
lms-plugins/mediacms-moodle/tiny/mediacms/mediacms/amd/build/embed.min.js
vendored
Executable file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
3
lms-plugins/mediacms-moodle/tiny/mediacms/mediacms/amd/build/embedmodal.min.js
vendored
Executable file
3
lms-plugins/mediacms-moodle/tiny/mediacms/mediacms/amd/build/embedmodal.min.js
vendored
Executable file
@@ -0,0 +1,3 @@
|
||||
define("tiny_mediacms/embedmodal",["exports","core/modal","./common"],(function(_exports,_modal,_common){var obj;function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_modal=(obj=_modal)&&obj.__esModule?obj:{default:obj};class EmbedModal extends _modal.default{registerEventListeners(){super.registerEventListeners(),this.registerCloseOnSave(),this.registerCloseOnCancel()}configure(modalConfig){modalConfig.large=!0,modalConfig.removeOnClose=!0,modalConfig.show=!0,super.configure(modalConfig)}}return _exports.default=EmbedModal,_defineProperty(EmbedModal,"TYPE","".concat(_common.component,"/modal")),_defineProperty(EmbedModal,"TEMPLATE","".concat(_common.component,"/embed_media_modal")),_exports.default}));
|
||||
|
||||
//# sourceMappingURL=embedmodal.min.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"embedmodal.min.js","sources":["../src/embedmodal.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Embedded Media Management Modal for Tiny.\n *\n * @module tiny_mediacms/embedmodal\n * @copyright 2022 Andrew Lyons <andrew@nicols.co.uk>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Modal from 'core/modal';\nimport {component} from './common';\n\nexport default class EmbedModal extends Modal {\n static TYPE = `${component}/modal`;\n static TEMPLATE = `${component}/embed_media_modal`;\n\n registerEventListeners() {\n // Call the parent registration.\n super.registerEventListeners();\n\n // Register to close on save/cancel.\n this.registerCloseOnSave();\n this.registerCloseOnCancel();\n }\n\n configure(modalConfig) {\n modalConfig.large = true;\n modalConfig.removeOnClose = true;\n modalConfig.show = true;\n\n super.configure(modalConfig);\n }\n}\n"],"names":["EmbedModal","Modal","registerEventListeners","registerCloseOnSave","registerCloseOnCancel","configure","modalConfig","large","removeOnClose","show","component"],"mappings":"iaA0BqBA,mBAAmBC,eAIpCC,+BAEUA,8BAGDC,2BACAC,wBAGTC,UAAUC,aACNA,YAAYC,OAAQ,EACpBD,YAAYE,eAAgB,EAC5BF,YAAYG,MAAO,QAEbJ,UAAUC,iEAlBHN,4BACAU,6CADAV,gCAEIU"}
|
||||
3
lms-plugins/mediacms-moodle/tiny/mediacms/mediacms/amd/build/iframeembed.min.js
vendored
Executable file
3
lms-plugins/mediacms-moodle/tiny/mediacms/mediacms/amd/build/iframeembed.min.js
vendored
Executable file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
3
lms-plugins/mediacms-moodle/tiny/mediacms/mediacms/amd/build/iframemodal.min.js
vendored
Executable file
3
lms-plugins/mediacms-moodle/tiny/mediacms/mediacms/amd/build/iframemodal.min.js
vendored
Executable file
@@ -0,0 +1,3 @@
|
||||
define("tiny_mediacms/iframemodal",["exports","core/modal","./common"],(function(_exports,_modal,_common){var obj;function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_modal=(obj=_modal)&&obj.__esModule?obj:{default:obj};class IframeModal extends _modal.default{registerEventListeners(){super.registerEventListeners(),this.registerCloseOnSave(),this.registerCloseOnCancel()}configure(modalConfig){modalConfig.large=!0,modalConfig.removeOnClose=!0,modalConfig.show=!0,super.configure(modalConfig)}}return _exports.default=IframeModal,_defineProperty(IframeModal,"TYPE","".concat(_common.component,"/iframemodal")),_defineProperty(IframeModal,"TEMPLATE","".concat(_common.component,"/iframe_embed_modal")),_exports.default}));
|
||||
|
||||
//# sourceMappingURL=iframemodal.min.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"iframemodal.min.js","sources":["../src/iframemodal.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Iframe Embed Modal for Tiny Media2.\n *\n * @module tiny_mediacms/iframemodal\n * @copyright 2024\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Modal from 'core/modal';\nimport {component} from './common';\n\nexport default class IframeModal extends Modal {\n static TYPE = `${component}/iframemodal`;\n static TEMPLATE = `${component}/iframe_embed_modal`;\n\n registerEventListeners() {\n // Call the parent registration.\n super.registerEventListeners();\n\n // Register to close on save/cancel.\n this.registerCloseOnSave();\n this.registerCloseOnCancel();\n }\n\n configure(modalConfig) {\n modalConfig.large = true;\n modalConfig.removeOnClose = true;\n modalConfig.show = true;\n\n super.configure(modalConfig);\n }\n}\n"],"names":["IframeModal","Modal","registerEventListeners","registerCloseOnSave","registerCloseOnCancel","configure","modalConfig","large","removeOnClose","show","component"],"mappings":"kaA0BqBA,oBAAoBC,eAIrCC,+BAEUA,8BAGDC,2BACAC,wBAGTC,UAAUC,aACNA,YAAYC,OAAQ,EACpBD,YAAYE,eAAgB,EAC5BF,YAAYG,MAAO,QAEbJ,UAAUC,kEAlBHN,6BACAU,mDADAV,iCAEIU"}
|
||||
3
lms-plugins/mediacms-moodle/tiny/mediacms/mediacms/amd/build/image.min.js
vendored
Executable file
3
lms-plugins/mediacms-moodle/tiny/mediacms/mediacms/amd/build/image.min.js
vendored
Executable file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
3
lms-plugins/mediacms-moodle/tiny/mediacms/mediacms/amd/build/imagedetails.min.js
vendored
Executable file
3
lms-plugins/mediacms-moodle/tiny/mediacms/mediacms/amd/build/imagedetails.min.js
vendored
Executable file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
10
lms-plugins/mediacms-moodle/tiny/mediacms/mediacms/amd/build/imagehelpers.min.js
vendored
Executable file
10
lms-plugins/mediacms-moodle/tiny/mediacms/mediacms/amd/build/imagehelpers.min.js
vendored
Executable file
@@ -0,0 +1,10 @@
|
||||
define("tiny_mediacms/imagehelpers",["exports","core/templates"],(function(_exports,_templates){var obj;
|
||||
/**
|
||||
* Tiny media plugin image helpers.
|
||||
*
|
||||
* @module tiny_mediacms/imagehelpers
|
||||
* @copyright 2024 Meirza <meirza.arson@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.showElements=_exports.isPercentageValue=_exports.hideElements=_exports.footerImageInsert=_exports.footerImageDetails=_exports.bodyImageInsert=_exports.bodyImageDetails=void 0,_templates=(obj=_templates)&&obj.__esModule?obj:{default:obj};_exports.bodyImageInsert=async(templateContext,root)=>_templates.default.renderForPromise("tiny_mediacms/insert_image_modal_insert",{...templateContext}).then((_ref=>{let{html:html,js:js}=_ref;_templates.default.replaceNodeContents(root.querySelector(".tiny_imagecms_body_template"),html,js)})).catch((error=>{window.console.log(error)}));_exports.footerImageInsert=async(templateContext,root)=>_templates.default.renderForPromise("tiny_mediacms/insert_image_modal_insert_footer",{...templateContext}).then((_ref2=>{let{html:html,js:js}=_ref2;_templates.default.replaceNodeContents(root.querySelector(".tiny_imagecms_footer_template"),html,js)})).catch((error=>{window.console.log(error)}));_exports.bodyImageDetails=async(templateContext,root)=>_templates.default.renderForPromise("tiny_mediacms/insert_image_modal_details",{...templateContext}).then((_ref3=>{let{html:html,js:js}=_ref3;_templates.default.replaceNodeContents(root.querySelector(".tiny_imagecms_body_template"),html,js)})).catch((error=>{window.console.log(error)}));_exports.footerImageDetails=async(templateContext,root)=>_templates.default.renderForPromise("tiny_mediacms/insert_image_modal_details_footer",{...templateContext}).then((_ref4=>{let{html:html,js:js}=_ref4;_templates.default.replaceNodeContents(root.querySelector(".tiny_imagecms_footer_template"),html,js)})).catch((error=>{window.console.log(error)}));_exports.showElements=(elements,root)=>{if(elements instanceof Array)elements.forEach((elementSelector=>{const element=root.querySelector(elementSelector);element&&element.classList.remove("d-none")}));else{const element=root.querySelector(elements);element&&element.classList.remove("d-none")}};_exports.hideElements=(elements,root)=>{if(elements instanceof Array)elements.forEach((elementSelector=>{const element=root.querySelector(elementSelector);element&&element.classList.add("d-none")}));else{const element=root.querySelector(elements);element&&element.classList.add("d-none")}};_exports.isPercentageValue=value=>value.match(/\d+%/)}));
|
||||
|
||||
//# sourceMappingURL=imagehelpers.min.js.map
|
||||
File diff suppressed because one or more lines are too long
3
lms-plugins/mediacms-moodle/tiny/mediacms/mediacms/amd/build/imageinsert.min.js
vendored
Executable file
3
lms-plugins/mediacms-moodle/tiny/mediacms/mediacms/amd/build/imageinsert.min.js
vendored
Executable file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
3
lms-plugins/mediacms-moodle/tiny/mediacms/mediacms/amd/build/imagemodal.min.js
vendored
Executable file
3
lms-plugins/mediacms-moodle/tiny/mediacms/mediacms/amd/build/imagemodal.min.js
vendored
Executable file
@@ -0,0 +1,3 @@
|
||||
define("tiny_mediacms/imagemodal",["exports","core/modal","./common"],(function(_exports,_modal,_common){var obj;function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_modal=(obj=_modal)&&obj.__esModule?obj:{default:obj};class ImageModal extends _modal.default{registerEventListeners(){super.registerEventListeners(),this.registerCloseOnSave(),this.registerCloseOnCancel()}configure(modalConfig){modalConfig.large=!0,modalConfig.removeOnClose=!0,modalConfig.show=!0,super.configure(modalConfig)}}return _exports.default=ImageModal,_defineProperty(ImageModal,"TYPE","".concat(_common.component,"/imagemodal")),_defineProperty(ImageModal,"TEMPLATE","".concat(_common.component,"/insert_image_modal")),ImageModal.registerModalType(),_exports.default}));
|
||||
|
||||
//# sourceMappingURL=imagemodal.min.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"imagemodal.min.js","sources":["../src/imagemodal.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Image Modal for Tiny.\n *\n * @module tiny_mediacms/imagemodal\n * @copyright 2022 Huong Nguyen <huongnv13@gmail.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Modal from 'core/modal';\nimport {component} from './common';\n\nexport default class ImageModal extends Modal {\n static TYPE = `${component}/imagemodal`;\n static TEMPLATE = `${component}/insert_image_modal`;\n\n registerEventListeners() {\n // Call the parent registration.\n super.registerEventListeners();\n\n // Register to close on save/cancel.\n this.registerCloseOnSave();\n this.registerCloseOnCancel();\n }\n\n configure(modalConfig) {\n modalConfig.large = true;\n modalConfig.removeOnClose = true;\n modalConfig.show = true;\n\n super.configure(modalConfig);\n }\n}\n\nImageModal.registerModalType();\n"],"names":["ImageModal","Modal","registerEventListeners","registerCloseOnSave","registerCloseOnCancel","configure","modalConfig","large","removeOnClose","show","component","registerModalType"],"mappings":"iaA0BqBA,mBAAmBC,eAIpCC,+BAEUA,8BAGDC,2BACAC,wBAGTC,UAAUC,aACNA,YAAYC,OAAQ,EACpBD,YAAYE,eAAgB,EAC5BF,YAAYG,MAAO,QAEbJ,UAAUC,iEAlBHN,4BACAU,kDADAV,gCAEIU,0CAoBzBV,WAAWW"}
|
||||
3
lms-plugins/mediacms-moodle/tiny/mediacms/mediacms/amd/build/manager.min.js
vendored
Executable file
3
lms-plugins/mediacms-moodle/tiny/mediacms/mediacms/amd/build/manager.min.js
vendored
Executable file
@@ -0,0 +1,3 @@
|
||||
define("tiny_mediacms/manager",["exports","core/templates","core/str","core/modal","core/modal_events","./options","core/config"],(function(_exports,_templates,_str,_modal,ModalEvents,_options,_config){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_templates=_interopRequireDefault(_templates),_modal=_interopRequireDefault(_modal),ModalEvents=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(ModalEvents),_config=_interopRequireDefault(_config);return _exports.default=class{constructor(editor){_defineProperty(this,"editor",null),_defineProperty(this,"area",null),this.editor=editor;const data=(0,_options.getData)(editor);this.area=data.params.area,this.area.itemid=data.fpoptions.image.itemid}async displayDialogue(){const modal=await _modal.default.create({large:!0,title:(0,_str.getString)("mediamanagerproperties","tiny_mediacms"),body:_templates.default.render("tiny_mediacms/mm2_iframe",{src:this.getIframeURL()}),removeOnClose:!0,show:!0});return modal.getRoot().on(ModalEvents.bodyRendered,(()=>{this.selectFirstElement()})),document.querySelector(".modal-lg").style.cssText="max-width: 850px",modal}selectFirstElement(){const iframe=document.getElementById("mm2-iframe");iframe.addEventListener("load",(function(){let intervalId=setInterval((function(){const iDocument=iframe.contentWindow.document;if(iDocument.querySelector(".filemanager")){const firstFocusableElement=iDocument.querySelector(".fp-navbar a:not([disabled])");firstFocusableElement&&firstFocusableElement.focus(),clearInterval(intervalId)}}),200)}))}getIframeURL(){const url=new URL("".concat(_config.default.wwwroot,"/lib/editor/tiny/plugins/mediacms/manage.php"));url.searchParams.append("elementid",this.editor.getElement().id);for(const key in this.area)url.searchParams.append(key,this.area[key]);return url.toString()}},_exports.default}));
|
||||
|
||||
//# sourceMappingURL=manager.min.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"manager.min.js","sources":["../src/manager.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Tiny Media Manager plugin class for Moodle.\n *\n * @module tiny_mediacms/manager\n * @copyright 2022, Stevani Andolo <stevani@hotmail.com.au>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Templates from 'core/templates';\nimport {getString} from 'core/str';\nimport Modal from 'core/modal';\nimport * as ModalEvents from 'core/modal_events';\nimport {getData} from './options';\nimport Config from 'core/config';\n\nexport default class MediaManager {\n\n editor = null;\n area = null;\n\n constructor(editor) {\n this.editor = editor;\n const data = getData(editor);\n this.area = data.params.area;\n this.area.itemid = data.fpoptions.image.itemid;\n }\n\n async displayDialogue() {\n const modal = await Modal.create({\n large: true,\n title: getString('mediamanagerproperties', 'tiny_mediacms'),\n body: Templates.render('tiny_mediacms/mm2_iframe', {\n src: this.getIframeURL()\n }),\n removeOnClose: true,\n show: true,\n });\n modal.getRoot().on(ModalEvents.bodyRendered, () => {\n this.selectFirstElement();\n });\n\n document.querySelector('.modal-lg').style.cssText = `max-width: 850px`;\n return modal;\n }\n\n // It will select the first element in the file manager.\n selectFirstElement() {\n const iframe = document.getElementById('mm2-iframe');\n iframe.addEventListener('load', function() {\n let intervalId = setInterval(function() {\n const iDocument = iframe.contentWindow.document;\n if (iDocument.querySelector('.filemanager')) {\n const firstFocusableElement = iDocument.querySelector('.fp-navbar a:not([disabled])');\n if (firstFocusableElement) {\n firstFocusableElement.focus();\n }\n clearInterval(intervalId);\n }\n }, 200);\n });\n }\n\n getIframeURL() {\n const url = new URL(`${Config.wwwroot}/lib/editor/tiny/plugins/mediacms/manage.php`);\n url.searchParams.append('elementid', this.editor.getElement().id);\n for (const key in this.area) {\n url.searchParams.append(key, this.area[key]);\n }\n return url.toString();\n }\n}\n"],"names":["constructor","editor","data","area","params","itemid","fpoptions","image","modal","Modal","create","large","title","body","Templates","render","src","this","getIframeURL","removeOnClose","show","getRoot","on","ModalEvents","bodyRendered","selectFirstElement","document","querySelector","style","cssText","iframe","getElementById","addEventListener","intervalId","setInterval","iDocument","contentWindow","firstFocusableElement","focus","clearInterval","url","URL","Config","wwwroot","searchParams","append","getElement","id","key","toString"],"mappings":"mmDAmCIA,YAAYC,sCAHH,kCACF,WAGEA,OAASA,aACRC,MAAO,oBAAQD,aAChBE,KAAOD,KAAKE,OAAOD,UACnBA,KAAKE,OAASH,KAAKI,UAAUC,MAAMF,qCAIlCG,YAAcC,eAAMC,OAAO,CAC7BC,OAAO,EACPC,OAAO,kBAAU,yBAA0B,iBAC3CC,KAAMC,mBAAUC,OAAO,2BAA4B,CAC/CC,IAAKC,KAAKC,iBAEdC,eAAe,EACfC,MAAM,WAEVZ,MAAMa,UAAUC,GAAGC,YAAYC,cAAc,UACpCC,wBAGTC,SAASC,cAAc,aAAaC,MAAMC,2BACnCrB,MAIXiB,2BACUK,OAASJ,SAASK,eAAe,cACvCD,OAAOE,iBAAiB,QAAQ,eACxBC,WAAaC,aAAY,iBACnBC,UAAYL,OAAOM,cAAcV,YACnCS,UAAUR,cAAc,gBAAiB,OACnCU,sBAAwBF,UAAUR,cAAc,gCAClDU,uBACAA,sBAAsBC,QAE1BC,cAAcN,eAEnB,QAIXf,qBACUsB,IAAM,IAAIC,cAAOC,gBAAOC,yDAC9BH,IAAII,aAAaC,OAAO,YAAa5B,KAAKhB,OAAO6C,aAAaC,QACzD,MAAMC,OAAO/B,KAAKd,KACnBqC,IAAII,aAAaC,OAAOG,IAAK/B,KAAKd,KAAK6C,aAEpCR,IAAIS"}
|
||||
11
lms-plugins/mediacms-moodle/tiny/mediacms/mediacms/amd/build/options.min.js
vendored
Executable file
11
lms-plugins/mediacms-moodle/tiny/mediacms/mediacms/amd/build/options.min.js
vendored
Executable file
@@ -0,0 +1,11 @@
|
||||
define("tiny_mediacms/options",["exports","editor_tiny/options","./common"],(function(_exports,_options,_common){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.register=_exports.getPermissions=_exports.getLti=_exports.getImagePermissions=_exports.getEmbedPermissions=_exports.getData=void 0;
|
||||
/**
|
||||
* Options helper for Tiny Media plugin.
|
||||
*
|
||||
* @module tiny_mediacms/options
|
||||
* @copyright 2022 Huong Nguyen <huongnv13@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
const dataName=(0,_options.getPluginOptionName)(_common.pluginName,"data"),permissionsName=(0,_options.getPluginOptionName)(_common.pluginName,"permissions"),ltiName=(0,_options.getPluginOptionName)(_common.pluginName,"lti");_exports.register=editor=>{const registerOption=editor.options.register;registerOption(permissionsName,{processor:"object",default:{image:{filepicker:!1}}}),registerOption(dataName,{processor:"object",default:{mediacmsApiUrl:"",mediacmsBaseUrl:"",mediacmsPageSize:12,autoConvertEnabled:!0,autoConvertBaseUrl:"",autoConvertOptions:{showTitle:!0,linkTitle:!0,showRelated:!0,showUserAvatar:!0}}}),registerOption(ltiName,{processor:"object",default:{toolId:0,courseId:0,contentItemUrl:""}})};const getPermissions=editor=>editor.options.get(permissionsName);_exports.getPermissions=getPermissions;_exports.getImagePermissions=editor=>getPermissions(editor).image;_exports.getEmbedPermissions=editor=>getPermissions(editor).embed;_exports.getData=editor=>editor.options.get(dataName);_exports.getLti=editor=>editor.options.get(ltiName)}));
|
||||
|
||||
//# sourceMappingURL=options.min.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"options.min.js","sources":["../src/options.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Options helper for Tiny Media plugin.\n *\n * @module tiny_mediacms/options\n * @copyright 2022 Huong Nguyen <huongnv13@gmail.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {getPluginOptionName} from 'editor_tiny/options';\nimport {pluginName} from './common';\n\nconst dataName = getPluginOptionName(pluginName, 'data');\nconst permissionsName = getPluginOptionName(pluginName, 'permissions');\nconst ltiName = getPluginOptionName(pluginName, 'lti');\n\n/**\n * Register the options for the Tiny Media plugin.\n *\n * @param {TinyMCE} editor\n */\nexport const register = (editor) => {\n const registerOption = editor.options.register;\n\n registerOption(permissionsName, {\n processor: 'object',\n \"default\": {\n image: {\n filepicker: false,\n }\n },\n });\n\n registerOption(dataName, {\n processor: 'object',\n \"default\": {\n // MediaCMS video library configuration\n mediacmsApiUrl: '', // e.g., 'https://deic.mediacms.io/api/v1/media'\n mediacmsBaseUrl: '', // e.g., 'https://deic.mediacms.io'\n mediacmsPageSize: 12,\n // Auto-conversion settings\n autoConvertEnabled: true, // Enable/disable auto-conversion of pasted MediaCMS URLs\n autoConvertBaseUrl: '', // Base URL to restrict auto-conversion (empty = allow all MediaCMS domains)\n autoConvertOptions: {\n // Default embed options for auto-converted videos\n showTitle: true,\n linkTitle: true,\n showRelated: true,\n showUserAvatar: true,\n },\n },\n });\n\n registerOption(ltiName, {\n processor: 'object',\n \"default\": {\n // LTI configuration for MediaCMS iframe library\n toolId: 0, // LTI external tool ID\n courseId: 0, // Current course ID\n contentItemUrl: '', // URL to /mod/lti/contentitem.php for Deep Linking\n },\n });\n};\n\n/**\n * Get the permissions configuration for the Tiny Media plugin.\n *\n * @param {TinyMCE} editor\n * @returns {object}\n */\nexport const getPermissions = (editor) => editor.options.get(permissionsName);\n\n/**\n * Get the permissions configuration for the Tiny Media plugin.\n *\n * @param {TinyMCE} editor\n * @returns {object}\n */\nexport const getImagePermissions = (editor) => getPermissions(editor).image;\n\n/**\n * Get the permissions configuration for the Tiny Media plugin.\n *\n * @param {TinyMCE} editor\n * @returns {object}\n */\nexport const getEmbedPermissions = (editor) => getPermissions(editor).embed;\n\n/**\n * Get the data configuration for the Media Manager.\n *\n * @param {TinyMCE} editor\n * @returns {object}\n */\nexport const getData = (editor) => editor.options.get(dataName);\n\n/**\n * Get the LTI configuration for the MediaCMS iframe library.\n *\n * @param {TinyMCE} editor\n * @returns {object}\n */\nexport const getLti = (editor) => editor.options.get(ltiName);\n"],"names":["dataName","pluginName","permissionsName","ltiName","editor","registerOption","options","register","processor","image","filepicker","mediacmsApiUrl","mediacmsBaseUrl","mediacmsPageSize","autoConvertEnabled","autoConvertBaseUrl","autoConvertOptions","showTitle","linkTitle","showRelated","showUserAvatar","toolId","courseId","contentItemUrl","getPermissions","get","embed"],"mappings":";;;;;;;;MA0BMA,UAAW,gCAAoBC,mBAAY,QAC3CC,iBAAkB,gCAAoBD,mBAAY,eAClDE,SAAU,gCAAoBF,mBAAY,yBAOvBG,eACfC,eAAiBD,OAAOE,QAAQC,SAEtCF,eAAeH,gBAAiB,CAC5BM,UAAW,iBACA,CACPC,MAAO,CACHC,YAAY,MAKxBL,eAAeL,SAAU,CACrBQ,UAAW,iBACA,CAEPG,eAAgB,GAChBC,gBAAiB,GACjBC,iBAAkB,GAElBC,oBAAoB,EACpBC,mBAAoB,GACpBC,mBAAoB,CAEhBC,WAAW,EACXC,WAAW,EACXC,aAAa,EACbC,gBAAgB,MAK5Bf,eAAeF,QAAS,CACpBK,UAAW,iBACA,CAEPa,OAAQ,EACRC,SAAU,EACVC,eAAgB,aAWfC,eAAkBpB,QAAWA,OAAOE,QAAQmB,IAAIvB,qFAQzBE,QAAWoB,eAAepB,QAAQK,mCAQlCL,QAAWoB,eAAepB,QAAQsB,uBAQ9CtB,QAAWA,OAAOE,QAAQmB,IAAIzB,0BAQ/BI,QAAWA,OAAOE,QAAQmB,IAAItB"}
|
||||
10
lms-plugins/mediacms-moodle/tiny/mediacms/mediacms/amd/build/plugin.min.js
vendored
Executable file
10
lms-plugins/mediacms-moodle/tiny/mediacms/mediacms/amd/build/plugin.min.js
vendored
Executable file
@@ -0,0 +1,10 @@
|
||||
define("tiny_mediacms/plugin",["exports","editor_tiny/loader","editor_tiny/utils","./common","./commands","./configuration","./options","./autoconvert"],(function(_exports,_loader,_utils,_common,Commands,Configuration,Options,_autoconvert){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}
|
||||
/**
|
||||
* Tiny Media plugin for Moodle.
|
||||
*
|
||||
* @module tiny_mediacms/plugin
|
||||
* @copyright 2022 Andrew Lyons <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,Commands=_interopRequireWildcard(Commands),Configuration=_interopRequireWildcard(Configuration),Options=_interopRequireWildcard(Options);var _default=new Promise((async resolve=>{const[tinyMCE,setupCommands,pluginMetadata]=await Promise.all([(0,_loader.getTinyMCE)(),Commands.getSetup(),(0,_utils.getPluginMetadata)(_common.component,_common.pluginName)]);tinyMCE.PluginManager.add("".concat(_common.component,"/plugin"),(editor=>(Options.register(editor),setupCommands(editor),(0,_autoconvert.setupAutoConvert)(editor),editor.on("GetContent",(e=>{if("html"===e.format){const tempDiv=document.createElement("div");tempDiv.innerHTML=e.content,tempDiv.querySelectorAll(".tiny-mediacms-edit-btn").forEach((btn=>btn.remove())),tempDiv.querySelectorAll(".tiny-mediacms-iframe-wrapper").forEach((wrapper=>{const iframe=wrapper.querySelector("iframe");iframe&&wrapper.parentNode.insertBefore(iframe,wrapper),wrapper.remove()})),tempDiv.querySelectorAll(".tiny-iframe-responsive").forEach((wrapper=>{const iframe=wrapper.querySelector("iframe");iframe&&wrapper.parentNode.insertBefore(iframe,wrapper),wrapper.remove()})),e.content=tempDiv.innerHTML}})),pluginMetadata))),resolve(["".concat(_common.component,"/plugin"),Configuration])}));return _exports.default=_default,_exports.default}));
|
||||
|
||||
//# sourceMappingURL=plugin.min.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"plugin.min.js","sources":["../src/plugin.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Tiny Media plugin for Moodle.\n *\n * @module tiny_mediacms/plugin\n * @copyright 2022 Andrew Lyons <andrew@nicols.co.uk>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport {getTinyMCE} from 'editor_tiny/loader';\nimport {getPluginMetadata} from 'editor_tiny/utils';\n\nimport {component, pluginName} from './common';\nimport * as Commands from './commands';\nimport * as Configuration from './configuration';\nimport * as Options from './options';\nimport {setupAutoConvert} from './autoconvert';\n\n// eslint-disable-next-line no-async-promise-executor\nexport default new Promise(async(resolve) => {\n const [\n tinyMCE,\n setupCommands,\n pluginMetadata,\n ] = await Promise.all([\n getTinyMCE(),\n Commands.getSetup(),\n getPluginMetadata(component, pluginName),\n ]);\n\n tinyMCE.PluginManager.add(`${component}/plugin`, (editor) => {\n // Register options.\n Options.register(editor);\n\n // Setup the Commands (buttons, menu items, and so on).\n setupCommands(editor);\n\n // Setup auto-conversion of pasted MediaCMS URLs.\n setupAutoConvert(editor);\n\n // Clean up editor-only elements before content is saved.\n // Remove wrapper divs and edit buttons that are only for the editor UI.\n editor.on('GetContent', (e) => {\n if (e.format === 'html') {\n // Create a temporary container to manipulate the HTML\n const tempDiv = document.createElement('div');\n tempDiv.innerHTML = e.content;\n\n // Remove edit buttons\n tempDiv.querySelectorAll('.tiny-mediacms-edit-btn').forEach(btn => btn.remove());\n\n // Unwrap iframes from tiny-mediacms-iframe-wrapper\n tempDiv.querySelectorAll('.tiny-mediacms-iframe-wrapper').forEach(wrapper => {\n const iframe = wrapper.querySelector('iframe');\n if (iframe) {\n wrapper.parentNode.insertBefore(iframe, wrapper);\n }\n wrapper.remove();\n });\n\n // Unwrap iframes from tiny-iframe-responsive\n tempDiv.querySelectorAll('.tiny-iframe-responsive').forEach(wrapper => {\n const iframe = wrapper.querySelector('iframe');\n if (iframe) {\n wrapper.parentNode.insertBefore(iframe, wrapper);\n }\n wrapper.remove();\n });\n\n e.content = tempDiv.innerHTML;\n }\n });\n\n return pluginMetadata;\n });\n\n // Resolve the Media Plugin and include configuration.\n resolve([`${component}/plugin`, Configuration]);\n});\n"],"names":["Promise","async","tinyMCE","setupCommands","pluginMetadata","all","Commands","getSetup","component","pluginName","PluginManager","add","editor","Options","register","on","e","format","tempDiv","document","createElement","innerHTML","content","querySelectorAll","forEach","btn","remove","wrapper","iframe","querySelector","parentNode","insertBefore","resolve","Configuration"],"mappings":";;;;;;;2OAgCe,IAAIA,SAAQC,MAAAA,gBAEnBC,QACAC,cACAC,sBACMJ,QAAQK,IAAI,EAClB,wBACAC,SAASC,YACT,4BAAkBC,kBAAWC,sBAGjCP,QAAQQ,cAAcC,cAAOH,8BAAqBI,SAE9CC,QAAQC,SAASF,QAGjBT,cAAcS,0CAGGA,QAIjBA,OAAOG,GAAG,cAAeC,OACJ,SAAbA,EAAEC,OAAmB,OAEfC,QAAUC,SAASC,cAAc,OACvCF,QAAQG,UAAYL,EAAEM,QAGtBJ,QAAQK,iBAAiB,2BAA2BC,SAAQC,KAAOA,IAAIC,WAGvER,QAAQK,iBAAiB,iCAAiCC,SAAQG,gBACxDC,OAASD,QAAQE,cAAc,UACjCD,QACAD,QAAQG,WAAWC,aAAaH,OAAQD,SAE5CA,QAAQD,YAIZR,QAAQK,iBAAiB,2BAA2BC,SAAQG,gBAClDC,OAASD,QAAQE,cAAc,UACjCD,QACAD,QAAQG,WAAWC,aAAaH,OAAQD,SAE5CA,QAAQD,YAGZV,EAAEM,QAAUJ,QAAQG,cAIrBjB,kBAIX4B,QAAQ,WAAIxB,6BAAoByB"}
|
||||
3
lms-plugins/mediacms-moodle/tiny/mediacms/mediacms/amd/build/selectors.min.js
vendored
Executable file
3
lms-plugins/mediacms-moodle/tiny/mediacms/mediacms/amd/build/selectors.min.js
vendored
Executable file
@@ -0,0 +1,3 @@
|
||||
define("tiny_mediacms/selectors",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;return _exports.default={IMAGE:{actions:{submit:".tiny_imagecms_urlentrysubmit",imageBrowser:".openimagecmsbrowser",addUrl:".tiny_imagecms_addurl",deleteImage:".tiny_imagecms_deleteicon"},elements:{form:"form.tiny_imagecms_form",alignSettings:".tiny_imagecms_button",alt:".tiny_imagecms_altentry",altWarning:".tiny_imagecms_altwarning",height:".tiny_imagecms_heightentry",width:".tiny_imagecms_widthentry",url:".tiny_imagecms_urlentry",urlWarning:".tiny_imagecms_urlwarning",size:".tiny_imagecms_size",presentation:".tiny_imagecms_presentation",constrain:".tiny_imagecms_constrain",customStyle:".tiny_imagecms_customstyle",preview:".tiny_imagecms_preview",previewBox:".tiny_imagecms_preview_box",loaderIcon:".tiny_imagecms_loader",loaderIconContainer:".tiny_imagecms_loader_container",insertImage:".tiny_imagecms_insert_image",modalFooter:".modal-footer",dropzoneContainer:".tiny_imagecms_dropzone_container",fileInput:"#tiny_imagecms_fileinput",fileNameLabel:".tiny_imagecms_filename",sizeOriginal:".tiny_imagecms_sizeoriginal",sizeCustom:".tiny_imagecms_sizecustom",properties:".tiny_imagecms_properties"},styles:{responsive:"img-fluid"}},EMBED:{actions:{submit:".tiny_mediacms_submit",mediaBrowser:".openmediacmsbrowser"},elements:{form:"form.tiny_mediacms_form",source:".tiny_mediacms_source",track:".tiny_mediacms_track",mediaSource:".tiny_mediacms_media_source",linkSource:".tiny_mediacms_link_source",linkSize:".tiny_mediacms_link_size",posterSource:".tiny_mediacms_poster_source",posterSize:".tiny_mediacms_poster_size",displayOptions:".tiny_mediacms_display_options",name:".tiny_mediacms_name_entry",title:".tiny_mediacms_title_entry",url:".tiny_mediacms_url_entry",width:".tiny_mediacms_width_entry",height:".tiny_mediacms_height_entry",trackSource:".tiny_mediacms_track_source",trackKind:".tiny_mediacms_track_kind_entry",trackLabel:".tiny_mediacms_track_label_entry",trackLang:".tiny_mediacms_track_lang_entry",trackDefault:".tiny_mediacms_track_default",mediaControl:".tiny_mediacms_controls",mediaAutoplay:".tiny_mediacms_autoplay",mediaMute:".tiny_mediacms_mute",mediaLoop:".tiny_mediacms_loop",advancedSettings:".tiny_mediacms_advancedsettings",linkTab:'li[data-medium-type="link"]',videoTab:'li[data-medium-type="video"]',audioTab:'li[data-medium-type="audio"]',linkPane:'.tab-pane[data-medium-type="link"]',videoPane:'.tab-pane[data-medium-type="video"]',audioPane:'.tab-pane[data-medium-type="audio"]',trackSubtitlesTab:'li[data-track-kind="subtitles"]',trackCaptionsTab:'li[data-track-kind="captions"]',trackDescriptionsTab:'li[data-track-kind="descriptions"]',trackChaptersTab:'li[data-track-kind="chapters"]',trackMetadataTab:'li[data-track-kind="metadata"]',trackSubtitlesPane:'.tab-pane[data-track-kind="subtitles"]',trackCaptionsPane:'.tab-pane[data-track-kind="captions"]',trackDescriptionsPane:'.tab-pane[data-track-kind="descriptions"]',trackChaptersPane:'.tab-pane[data-track-kind="chapters"]',trackMetadataPane:'.tab-pane[data-track-kind="metadata"]'},mediaTypes:{link:"LINK",video:"VIDEO",audio:"AUDIO"},trackKinds:{subtitles:"SUBTITLES",captions:"CAPTIONS",descriptions:"DESCRIPTIONS",chapters:"CHAPTERS",metadata:"METADATA"}},IFRAME:{actions:{remove:'[data-action="remove"]'},elements:{form:"form.tiny_iframecms_form",url:".tiny_iframecms_url",urlWarning:".tiny_iframecms_url_warning",showTitle:".tiny_iframecms_showtitle",linkTitle:".tiny_iframecms_linktitle",showRelated:".tiny_iframecms_showrelated",showUserAvatar:".tiny_iframecms_showuseravatar",responsive:".tiny_iframecms_responsive",startAt:".tiny_iframecms_startat",startAtEnabled:".tiny_iframecms_startat_enabled",aspectRatio:".tiny_iframecms_aspectratio",width:".tiny_iframecms_width",height:".tiny_iframecms_height",preview:".tiny_iframecms_preview",previewContainer:".tiny_iframecms_preview_container",tabs:".tiny_iframecms_tabs",tabUrlBtn:".tiny_iframecms_tab_url_btn",tabIframeLibraryBtn:".tiny_iframecms_tab_iframe_library_btn",paneUrl:".tiny_iframecms_pane_url",paneIframeLibrary:".tiny_iframecms_pane_iframe_library",iframeLibraryContainer:".tiny_iframecms_iframe_library_container",iframeLibraryPlaceholder:".tiny_iframecms_iframe_library_placeholder",iframeLibraryLoading:".tiny_iframecms_iframe_library_loading",iframeLibraryFrame:".tiny_iframecms_iframe_library_frame"},aspectRatios:{"16:9":{width:560,height:315},"4:3":{width:560,height:420},"1:1":{width:400,height:400},custom:null}}},_exports.default}));
|
||||
|
||||
//# sourceMappingURL=selectors.min.js.map
|
||||
File diff suppressed because one or more lines are too long
10
lms-plugins/mediacms-moodle/tiny/mediacms/mediacms/amd/build/usedfiles.min.js
vendored
Executable file
10
lms-plugins/mediacms-moodle/tiny/mediacms/mediacms/amd/build/usedfiles.min.js
vendored
Executable file
@@ -0,0 +1,10 @@
|
||||
define("tiny_mediacms/usedfiles",["exports","core/templates","core/config"],(function(_exports,Templates,_config){var obj;function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,Templates=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}
|
||||
/**
|
||||
* Tiny Media Manager usedfiles.
|
||||
*
|
||||
* @module tiny_mediacms/usedfiles
|
||||
* @copyright 2022, Stevani Andolo <stevani@hotmail.com.au>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/(Templates),_config=(obj=_config)&&obj.__esModule?obj:{default:obj};class UsedFileManager{constructor(files,userContext,itemId,elementId){this.files=files,this.userContext=userContext,this.itemId=itemId,this.elementId=elementId}getElementId(){return this.elementId}getUsedFiles(){const editor=window.parent.tinymce.EditorManager.get(this.getElementId());if(!editor)return window.console.error("Editor not found for ".concat(this.getElementId())),[];const content=editor.getContent(),baseUrl="".concat(_config.default.wwwroot,"/draftfile.php/").concat(this.userContext,"/user/draft/").concat(this.itemId,"/"),pattern=new RegExp("[\"']"+baseUrl.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&")+"(?<filename>.+?)[\\?\"']","gm");return[...content.matchAll(pattern)].map((match=>decodeURIComponent(match.groups.filename)))}findUnusedFiles(usedFiles){return Object.entries(this.files).filter((_ref=>{let[filename]=_ref;return!usedFiles.includes(filename)})).map((_ref2=>{let[filename]=_ref2;return filename}))}findMissingFiles(usedFiles){return usedFiles.filter((filename=>!this.files.hasOwnProperty(filename)))}updateFiles(){const form=document.querySelector("form"),usedFiles=this.getUsedFiles(),unusedFiles=this.findUnusedFiles(usedFiles),missingFiles=this.findMissingFiles(usedFiles);return form.querySelectorAll('input[type=checkbox][name^="deletefile"]').forEach((checkbox=>{unusedFiles.includes(checkbox.dataset.filename)||checkbox.closest(".fitem").remove()})),form.classList.toggle("has-missing-files",!!missingFiles.length),form.classList.toggle("has-unused-files",!!unusedFiles.length),Templates.renderForPromise("tiny_mediacms/missingfiles",{missingFiles:missingFiles}).then((_ref3=>{let{html:html,js:js}=_ref3;Templates.replaceNodeContents(form.querySelector(".missing-files"),html,js)}))}}_exports.init=(files,usercontext,itemid,elementid)=>{const manager=new UsedFileManager(files,usercontext,itemid,elementid);return manager.updateFiles(),manager}}));
|
||||
|
||||
//# sourceMappingURL=usedfiles.min.js.map
|
||||
File diff suppressed because one or more lines are too long
265
lms-plugins/mediacms-moodle/tiny/mediacms/mediacms/amd/src/autoconvert.js
Executable file
265
lms-plugins/mediacms-moodle/tiny/mediacms/mediacms/amd/src/autoconvert.js
Executable file
@@ -0,0 +1,265 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Tiny MediaCMS Auto-convert module.
|
||||
*
|
||||
* This module automatically converts pasted MediaCMS URLs into embedded videos.
|
||||
* When a user pastes a MediaCMS video URL (e.g., https://deic.mediacms.io/view?m=JpBd1Zvdl),
|
||||
* it will be automatically converted to an iframe embed.
|
||||
*
|
||||
* @module tiny_mediacms/autoconvert
|
||||
* @copyright 2024
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
import {getData} from './options';
|
||||
|
||||
/**
|
||||
* Regular expression patterns for MediaCMS URLs.
|
||||
* Matches URLs like:
|
||||
* - https://deic.mediacms.io/view?m=JpBd1Zvdl
|
||||
* - https://example.mediacms.io/view?m=VIDEO_ID
|
||||
* - Custom domains configured in the plugin
|
||||
*/
|
||||
const MEDIACMS_VIEW_URL_PATTERN = /^(https?:\/\/[^\/]+)\/view\?m=([a-zA-Z0-9_-]+)$/;
|
||||
|
||||
/**
|
||||
* Check if a string is a valid MediaCMS view URL.
|
||||
*
|
||||
* @param {string} text - The text to check
|
||||
* @returns {Object|null} - Parsed URL info or null if not a valid MediaCMS URL
|
||||
*/
|
||||
const parseMediaCMSUrl = (text) => {
|
||||
if (!text || typeof text !== 'string') {
|
||||
return null;
|
||||
}
|
||||
|
||||
const trimmed = text.trim();
|
||||
|
||||
// Check for MediaCMS view URL pattern
|
||||
const match = trimmed.match(MEDIACMS_VIEW_URL_PATTERN);
|
||||
if (match) {
|
||||
return {
|
||||
baseUrl: match[1],
|
||||
videoId: match[2],
|
||||
originalUrl: trimmed,
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the pasted URL's domain is allowed based on configuration.
|
||||
*
|
||||
* @param {Object} parsed - Parsed URL info
|
||||
* @param {Object} config - Plugin configuration
|
||||
* @returns {boolean} - True if the domain is allowed
|
||||
*/
|
||||
const isDomainAllowed = (parsed, config) => {
|
||||
// If no specific base URL is configured, allow all MediaCMS domains
|
||||
const configuredBaseUrl = config.autoConvertBaseUrl || config.mediacmsBaseUrl;
|
||||
if (!configuredBaseUrl) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if the URL's base matches the configured base URL
|
||||
try {
|
||||
const configuredUrl = new URL(configuredBaseUrl);
|
||||
const pastedUrl = new URL(parsed.baseUrl);
|
||||
return configuredUrl.host === pastedUrl.host;
|
||||
} catch (e) {
|
||||
// If URL parsing fails, allow the conversion
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate the iframe embed HTML for a MediaCMS video.
|
||||
*
|
||||
* @param {Object} parsed - Parsed URL info
|
||||
* @param {Object} options - Embed options
|
||||
* @returns {string} - The iframe HTML
|
||||
*/
|
||||
const generateEmbedHtml = (parsed, options = {}) => {
|
||||
// Build the embed URL with default options
|
||||
const embedUrl = new URL(`${parsed.baseUrl}/embed`);
|
||||
embedUrl.searchParams.set('m', parsed.videoId);
|
||||
|
||||
// Apply default options (all enabled by default for best user experience)
|
||||
embedUrl.searchParams.set('showTitle', options.showTitle !== false ? '1' : '0');
|
||||
embedUrl.searchParams.set('showRelated', options.showRelated !== false ? '1' : '0');
|
||||
embedUrl.searchParams.set('showUserAvatar', options.showUserAvatar !== false ? '1' : '0');
|
||||
embedUrl.searchParams.set('linkTitle', options.linkTitle !== false ? '1' : '0');
|
||||
|
||||
// Generate clean iframe HTML (wrapper will be added by editor for UI, then stripped on save)
|
||||
const html = `<iframe ` +
|
||||
`width="400" height="300" ` +
|
||||
`style="display: block; border: 0;" ` +
|
||||
`src="${embedUrl.toString()}" ` +
|
||||
`allowfullscreen="allowfullscreen">` +
|
||||
`</iframe>`;
|
||||
|
||||
return html;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set up auto-conversion for the editor.
|
||||
* This registers event handlers to detect pasted MediaCMS URLs.
|
||||
*
|
||||
* @param {TinyMCE} editor - The TinyMCE editor instance
|
||||
*/
|
||||
export const setupAutoConvert = (editor) => {
|
||||
const config = getData(editor) || {};
|
||||
|
||||
// Check if auto-convert is enabled (default: true)
|
||||
if (config.autoConvertEnabled === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle paste events
|
||||
editor.on('paste', (e) => {
|
||||
handlePasteEvent(editor, e, config);
|
||||
});
|
||||
|
||||
// Also handle input events for drag-and-drop text or keyboard paste
|
||||
editor.on('input', (e) => {
|
||||
handleInputEvent(editor, e, config);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle paste events to detect and convert MediaCMS URLs.
|
||||
*
|
||||
* @param {TinyMCE} editor - The TinyMCE editor instance
|
||||
* @param {Event} e - The paste event
|
||||
* @param {Object} config - Plugin configuration
|
||||
*/
|
||||
const handlePasteEvent = (editor, e, config) => {
|
||||
// Get pasted text from clipboard
|
||||
const clipboardData = e.clipboardData || window.clipboardData;
|
||||
if (!clipboardData) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to get plain text first
|
||||
const text = clipboardData.getData('text/plain') || clipboardData.getData('text');
|
||||
if (!text) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if it's a MediaCMS URL
|
||||
const parsed = parseMediaCMSUrl(text);
|
||||
if (!parsed) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if domain is allowed
|
||||
if (!isDomainAllowed(parsed, config)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevent default paste behavior
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
// Generate and insert the embed HTML
|
||||
const embedHtml = generateEmbedHtml(parsed, config.autoConvertOptions || {});
|
||||
|
||||
// Use a slight delay to ensure the editor is ready
|
||||
setTimeout(() => {
|
||||
editor.insertContent(embedHtml);
|
||||
// Move cursor after the inserted content
|
||||
editor.selection.collapse(false);
|
||||
}, 0);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle input events to catch URLs that might have been pasted without triggering paste event.
|
||||
* This is a fallback for certain browsers/scenarios.
|
||||
*
|
||||
* @param {TinyMCE} editor - The TinyMCE editor instance
|
||||
* @param {Event} e - The input event
|
||||
* @param {Object} config - Plugin configuration
|
||||
*/
|
||||
const handleInputEvent = (editor, e, config) => {
|
||||
// Only process inputType 'insertFromPaste' if paste event didn't catch it
|
||||
if (e.inputType !== 'insertFromPaste' && e.inputType !== 'insertText') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the current node and check if it contains just a URL
|
||||
const node = editor.selection.getNode();
|
||||
if (!node || node.nodeName !== 'P') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the paragraph contains only a MediaCMS URL
|
||||
const text = node.textContent || '';
|
||||
const parsed = parseMediaCMSUrl(text);
|
||||
|
||||
if (!parsed || !isDomainAllowed(parsed, config)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't convert if there's other content in the paragraph
|
||||
const trimmedHtml = node.innerHTML.trim();
|
||||
if (trimmedHtml !== text.trim() && !trimmedHtml.startsWith(text.trim())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate the embed HTML
|
||||
const embedHtml = generateEmbedHtml(parsed, config.autoConvertOptions || {});
|
||||
|
||||
// Replace the paragraph content with the embed
|
||||
// Use a slight delay to let the input event complete
|
||||
setTimeout(() => {
|
||||
// Re-check that the node still contains the URL (user might have typed more)
|
||||
const currentText = node.textContent || '';
|
||||
const currentParsed = parseMediaCMSUrl(currentText);
|
||||
|
||||
if (currentParsed && currentParsed.originalUrl === parsed.originalUrl) {
|
||||
// Select and replace the entire node
|
||||
editor.selection.select(node);
|
||||
editor.insertContent(embedHtml);
|
||||
}
|
||||
}, 100);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if a text is a MediaCMS URL (public helper).
|
||||
*
|
||||
* @param {string} text - The text to check
|
||||
* @returns {boolean} - True if it's a MediaCMS URL
|
||||
*/
|
||||
export const isMediaCMSUrl = (text) => {
|
||||
return parseMediaCMSUrl(text) !== null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert a MediaCMS URL to embed HTML (public helper).
|
||||
*
|
||||
* @param {string} url - The MediaCMS URL
|
||||
* @param {Object} options - Embed options
|
||||
* @returns {string|null} - The embed HTML or null if not a valid URL
|
||||
*/
|
||||
export const convertToEmbed = (url, options = {}) => {
|
||||
const parsed = parseMediaCMSUrl(url);
|
||||
if (!parsed) {
|
||||
return null;
|
||||
}
|
||||
return generateEmbedHtml(parsed, options);
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user