Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
YUI.add('moodle-atto_media-button', function (Y, NAME) {
2
 
3
// This file is part of Moodle - http://moodle.org/
4
//
5
// Moodle is free software: you can redistribute it and/or modify
6
// it under the terms of the GNU General Public License as published by
7
// the Free Software Foundation, either version 3 of the License, or
8
// (at your option) any later version.
9
//
10
// Moodle is distributed in the hope that it will be useful,
11
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
// GNU General Public License for more details.
14
//
15
// You should have received a copy of the GNU General Public License
16
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
17
 
18
/*
19
 * @package    atto_media
20
 * @copyright  2013 Damyon Wiese  <damyon@moodle.com>
21
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
22
 */
23
 
24
/**
25
 * @module moodle-atto_media-button
26
 */
27
 
28
/**
29
 * Atto media selection tool.
30
 *
31
 * @namespace M.atto_media
32
 * @class Button
33
 * @extends M.editor_atto.EditorPlugin
34
 */
35
 
36
var COMPONENTNAME = 'atto_media',
37
    MEDIA_TYPES = {LINK: 'LINK', VIDEO: 'VIDEO', AUDIO: 'AUDIO'},
38
    TRACK_KINDS = {
39
        SUBTITLES: 'SUBTITLES',
40
        CAPTIONS: 'CAPTIONS',
41
        DESCRIPTIONS: 'DESCRIPTIONS',
42
        CHAPTERS: 'CHAPTERS',
43
        METADATA: 'METADATA'
44
    },
45
    CSS = {
46
        SOURCE: 'atto_media_source',
47
        TRACK: 'atto_media_track',
48
        MEDIA_SOURCE: 'atto_media_media_source',
49
        LINK_SOURCE: 'atto_media_link_source',
50
        POSTER_SOURCE: 'atto_media_poster_source',
51
        TRACK_SOURCE: 'atto_media_track_source',
52
        DISPLAY_OPTIONS: 'atto_media_display_options',
53
        NAME_INPUT: 'atto_media_name_entry',
54
        TITLE_INPUT: 'atto_media_title_entry',
55
        URL_INPUT: 'atto_media_url_entry',
56
        POSTER_SIZE: 'atto_media_poster_size',
57
        LINK_SIZE: 'atto_media_link_size',
58
        WIDTH_INPUT: 'atto_media_width_entry',
59
        HEIGHT_INPUT: 'atto_media_height_entry',
60
        TRACK_KIND_INPUT: 'atto_media_track_kind_entry',
61
        TRACK_LABEL_INPUT: 'atto_media_track_label_entry',
62
        TRACK_LANG_INPUT: 'atto_media_track_lang_entry',
63
        TRACK_DEFAULT_SELECT: 'atto_media_track_default',
64
        MEDIA_CONTROLS_TOGGLE: 'atto_media_controls',
65
        MEDIA_AUTOPLAY_TOGGLE: 'atto_media_autoplay',
66
        MEDIA_MUTE_TOGGLE: 'atto_media_mute',
67
        MEDIA_LOOP_TOGGLE: 'atto_media_loop',
68
        ADVANCED_SETTINGS: 'atto_media_advancedsettings',
69
        LINK: MEDIA_TYPES.LINK.toLowerCase(),
70
        VIDEO: MEDIA_TYPES.VIDEO.toLowerCase(),
71
        AUDIO: MEDIA_TYPES.AUDIO.toLowerCase(),
72
        TRACK_SUBTITLES: TRACK_KINDS.SUBTITLES.toLowerCase(),
73
        TRACK_CAPTIONS: TRACK_KINDS.CAPTIONS.toLowerCase(),
74
        TRACK_DESCRIPTIONS: TRACK_KINDS.DESCRIPTIONS.toLowerCase(),
75
        TRACK_CHAPTERS: TRACK_KINDS.CHAPTERS.toLowerCase(),
76
        TRACK_METADATA: TRACK_KINDS.METADATA.toLowerCase()
77
    },
78
    SELECTORS = {
79
        SOURCE: '.' + CSS.SOURCE,
80
        TRACK: '.' + CSS.TRACK,
81
        MEDIA_SOURCE: '.' + CSS.MEDIA_SOURCE,
82
        POSTER_SOURCE: '.' + CSS.POSTER_SOURCE,
83
        TRACK_SOURCE: '.' + CSS.TRACK_SOURCE,
84
        DISPLAY_OPTIONS: '.' + CSS.DISPLAY_OPTIONS,
85
        NAME_INPUT: '.' + CSS.NAME_INPUT,
86
        TITLE_INPUT: '.' + CSS.TITLE_INPUT,
87
        URL_INPUT: '.' + CSS.URL_INPUT,
88
        POSTER_SIZE: '.' + CSS.POSTER_SIZE,
89
        LINK_SIZE: '.' + CSS.LINK_SIZE,
90
        WIDTH_INPUT: '.' + CSS.WIDTH_INPUT,
91
        HEIGHT_INPUT: '.' + CSS.HEIGHT_INPUT,
92
        TRACK_KIND_INPUT: '.' + CSS.TRACK_KIND_INPUT,
93
        TRACK_LABEL_INPUT: '.' + CSS.TRACK_LABEL_INPUT,
94
        TRACK_LANG_INPUT: '.' + CSS.TRACK_LANG_INPUT,
95
        TRACK_DEFAULT_SELECT: '.' + CSS.TRACK_DEFAULT_SELECT,
96
        MEDIA_CONTROLS_TOGGLE: '.' + CSS.MEDIA_CONTROLS_TOGGLE,
97
        MEDIA_AUTOPLAY_TOGGLE: '.' + CSS.MEDIA_AUTOPLAY_TOGGLE,
98
        MEDIA_MUTE_TOGGLE: '.' + CSS.MEDIA_MUTE_TOGGLE,
99
        MEDIA_LOOP_TOGGLE: '.' + CSS.MEDIA_LOOP_TOGGLE,
100
        ADVANCED_SETTINGS: '.' + CSS.ADVANCED_SETTINGS,
101
        LINK_TAB: 'li[data-medium-type="' + CSS.LINK + '"]',
102
        LINK_PANE: '.tab-pane[data-medium-type="' + CSS.LINK + '"]',
103
        VIDEO_TAB: 'li[data-medium-type="' + CSS.VIDEO + '"]',
104
        VIDEO_PANE: '.tab-pane[data-medium-type="' + CSS.VIDEO + '"]',
105
        AUDIO_TAB: 'li[data-medium-type="' + CSS.AUDIO + '"]',
106
        AUDIO_PANE: '.tab-pane[data-medium-type="' + CSS.AUDIO + '"]',
107
        TRACK_SUBTITLES_TAB: 'li[data-track-kind="' + CSS.TRACK_SUBTITLES + '"]',
108
        TRACK_SUBTITLES_PANE: '.tab-pane[data-track-kind="' + CSS.TRACK_SUBTITLES + '"]',
109
        TRACK_CAPTIONS_TAB: 'li[data-track-kind="' + CSS.TRACK_CAPTIONS + '"]',
110
        TRACK_CAPTIONS_PANE: '.tab-pane[data-track-kind="' + CSS.TRACK_CAPTIONS + '"]',
111
        TRACK_DESCRIPTIONS_TAB: 'li[data-track-kind="' + CSS.TRACK_DESCRIPTIONS + '"]',
112
        TRACK_DESCRIPTIONS_PANE: '.tab-pane[data-track-kind="' + CSS.TRACK_DESCRIPTIONS + '"]',
113
        TRACK_CHAPTERS_TAB: 'li[data-track-kind="' + CSS.TRACK_CHAPTERS + '"]',
114
        TRACK_CHAPTERS_PANE: '.tab-pane[data-track-kind="' + CSS.TRACK_CHAPTERS + '"]',
115
        TRACK_METADATA_TAB: 'li[data-track-kind="' + CSS.TRACK_METADATA + '"]',
116
        TRACK_METADATA_PANE: '.tab-pane[data-track-kind="' + CSS.TRACK_METADATA + '"]'
117
    },
118
    TEMPLATES = {
119
        ROOT: '' +
120
            '<form class="mform atto_form atto_media" id="{{elementid}}_atto_media_form">' +
121
                '<ul class="root nav nav-tabs mb-1" role="tablist">' +
122
                    '<li data-medium-type="{{CSS.LINK}}" class="nav-item">' +
123
                        '<a class="nav-link active" href="#{{elementid}}_{{CSS.LINK}}" role="tab" data-toggle="tab">' +
124
                            '{{get_string "link" component}}' +
125
                        '</a>' +
126
                    '</li>' +
127
                    '<li data-medium-type="{{CSS.VIDEO}}" class="nav-item">' +
128
                        '<a class="nav-link" href="#{{elementid}}_{{CSS.VIDEO}}" role="tab" data-toggle="tab">' +
129
                            '{{get_string "video" component}}' +
130
                        '</a>' +
131
                    '</li>' +
132
                    '<li data-medium-type="{{CSS.AUDIO}}" class="nav-item">' +
133
                        '<a class="nav-link" href="#{{elementid}}_{{CSS.AUDIO}}" role="tab" data-toggle="tab">' +
134
                            '{{get_string "audio" component}}' +
135
                        '</a>' +
136
                    '</li>' +
137
                '</ul>' +
138
                '<div class="root tab-content">' +
139
                    '<div data-medium-type="{{CSS.LINK}}" class="tab-pane active" id="{{elementid}}_{{CSS.LINK}}">' +
140
                        '{{> tab_panes.link}}' +
141
                    '</div>' +
142
                    '<div data-medium-type="{{CSS.VIDEO}}" class="tab-pane" id="{{elementid}}_{{CSS.VIDEO}}">' +
143
                        '{{> tab_panes.video}}' +
144
                    '</div>' +
145
                    '<div data-medium-type="{{CSS.AUDIO}}" class="tab-pane" id="{{elementid}}_{{CSS.AUDIO}}">' +
146
                        '{{> tab_panes.audio}}' +
147
                    '</div>' +
148
                '</div>' +
149
                '<div class="mdl-align">' +
150
                    '<br/>' +
151
                    '<button class="btn btn-secondary submit" type="submit">{{get_string "createmedia" component}}</button>' +
152
                '</div>' +
153
            '</form>',
154
        TAB_PANES: {
155
            LINK: '' +
156
                '{{renderPartial "form_components.source" context=this id=CSS.LINK_SOURCE}}' +
157
                '<label for="{{elementid}}_link_nameentry">{{get_string "entername" component}}</label>' +
158
                '<input class="form-control fullwidth {{CSS.NAME_INPUT}}" type="text" id="{{elementid}}_link_nameentry"' +
159
                        'size="32" required="true"/>',
160
            VIDEO: '' +
161
                '{{renderPartial "form_components.source" context=this id=CSS.MEDIA_SOURCE entersourcelabel="videosourcelabel"' +
162
                    ' addcomponentlabel="addsource" multisource="true" addsourcehelp=helpStrings.addsource}}' +
163
                '<fieldset class="collapsible collapsed" id="{{elementid}}_video-display-options">' +
164
                    '<input name="mform_isexpanded_{{elementid}}_video-display-options" type="hidden">' +
165
                    '{{renderPartial "form_components.section" context=this id="vdisplayoptions" name="displayoptions"}}' +
166
                    '<div id="vdisplayoptions" class="fcontainer collapseable collapse px-1">' +
167
                        '{{renderPartial "form_components.display_options" context=this id=CSS.VIDEO mediatype_video=true}}' +
168
                    '</div>' +
169
                '</fieldset>' +
170
                '<fieldset class="collapsible collapsed" id="{{elementid}}_video-advanced-settings">' +
171
                    '<input name="mform_isexpanded_{{elementid}}_video-advanced-settings" type="hidden">' +
172
                    '{{renderPartial "form_components.section" context=this id="vadvancedsettings" name="advancedsettings"}}' +
173
                    '<div id="vadvancedsettings" class="fcontainer collapseable collapse px-1">' +
174
                        '{{renderPartial "form_components.advanced_settings" context=this id=CSS.VIDEO}}' +
175
                    '</div>' +
176
                '</fieldset>' +
177
                '<fieldset class="collapsible collapsed" id="{{elementid}}_video-tracks">' +
178
                    '<input name="mform_isexpanded_{{elementid}}_video-tracks" type="hidden">' +
179
                    '{{renderPartial "form_components.section" context=this id="vtracks" name="tracks" help=helpStrings.tracks}}' +
180
                    '<div id="vtracks" class="fcontainer collapseable collapse px-1">' +
181
                        '{{renderPartial "form_components.track_tabs" context=this id=CSS.VIDEO}}' +
182
                    '</div>' +
183
                '</fieldset>',
184
            AUDIO: '' +
185
                '{{renderPartial "form_components.source" context=this id=CSS.MEDIA_SOURCE entersourcelabel="audiosourcelabel"' +
186
                    ' addcomponentlabel="addsource" multisource="true" addsourcehelp=helpStrings.addsource}}' +
187
                '<fieldset class="collapsible collapsed" id="{{elementid}}_audio-display-options">' +
188
                    '<input name="mform_isexpanded_{{elementid}}_audio-display-options" type="hidden">' +
189
                    '{{renderPartial "form_components.section" context=this id="adisplayoptions" name="displayoptions"}}' +
190
                    '<div id="adisplayoptions" class="fcontainer collapseable collapse px-1">' +
191
                        '{{renderPartial "form_components.display_options" context=this id=CSS.AUDIO}}' +
192
                    '</div>' +
193
                '</fieldset>' +
194
                '<fieldset class="collapsible collapsed" id="{{elementid}}_audio-advanced-settings">' +
195
                    '<input name="mform_isexpanded_{{elementid}}_audio-advanced-settings" type="hidden">' +
196
                    '{{renderPartial "form_components.section" context=this id="aadvancedsettings" name="advancedsettings"}}' +
197
                    '<div id="aadvancedsettings" class="fcontainer collapseable collapse px-1">' +
198
                        '{{renderPartial "form_components.advanced_settings" context=this id=CSS.AUDIO}}' +
199
                    '</div>' +
200
                '</fieldset>' +
201
                '<fieldset class="collapsible collapsed" id="{{elementid}}_audio-tracks">' +
202
                    '<input name="mform_isexpanded_{{elementid}}_audio-tracks" type="hidden">' +
203
                    '{{renderPartial "form_components.section" context=this id="atracks" name="tracks" help=helpStrings.tracks}}' +
204
                    '<div id="atracks" class="fcontainer collapseable collapse px-1">' +
205
                        '{{renderPartial "form_components.track_tabs" context=this id=CSS.AUDIO}}' +
206
                    '</div>' +
207
                '</fieldset>'
208
        },
209
        FORM_COMPONENTS: {
210
            SOURCE: '' +
211
                '<div class="{{CSS.SOURCE}} {{id}}">' +
212
                    '<div class="mb-1">' +
213
                        '<label for="url-input">' +
214
                        '{{#entersourcelabel}}{{get_string ../entersourcelabel ../component}}{{/entersourcelabel}}' +
215
                        '{{^entersourcelabel}}{{get_string "entersource" ../component}}{{/entersourcelabel}}' +
216
                        '</label>' +
217
                        '<div class="input-group input-append w-100">' +
218
                            '<input id="url-input" class="form-control {{CSS.URL_INPUT}}" type="url" size="32"/>' +
219
                            '<span class="input-group-append">' +
220
                                '<button class="btn btn-secondary openmediabrowser" type="button">' +
221
                                '{{get_string "browserepositories" component}}</button>' +
222
                            '</span>' +
223
                        '</div>' +
224
                    '</div>' +
225
                    '{{#multisource}}' +
226
                        '{{renderPartial "form_components.add_component" context=../this label=../addcomponentlabel ' +
227
                            ' help=../addsourcehelp}}' +
228
                    '{{/multisource}}' +
229
                '</div>',
230
            ADD_COMPONENT: '' +
231
                '<div>' +
232
                    '<a href="#" class="addcomponent">' +
233
                        '{{#label}}{{get_string ../label ../component}}{{/label}}' +
234
                        '{{^label}}{{get_string "add" ../component}}{{/label}}' +
235
                    '</a>' +
236
                    '{{#help}}{{{../help}}}{{/help}}' +
237
                '</div>',
238
            REMOVE_COMPONENT: '' +
239
                '<div>' +
240
                    '<a href="#" class="removecomponent">' +
241
                        '{{#label}}{{get_string ../label ../component}}{{/label}}' +
242
                        '{{^label}}{{get_string "remove" ../component}}{{/label}}' +
243
                    '</a>' +
244
                '</div>',
245
            DISPLAY_OPTIONS: '' +
246
                '<div class="{{CSS.DISPLAY_OPTIONS}}">' +
247
                    '<div class="mb-1">' +
248
                        '<label for="{{id}}_media-title-entry">{{get_string "entertitle" component}}</label>' +
249
                        '<input class="form-control fullwidth {{CSS.TITLE_INPUT}}" type="text" id="{{id}}_media-title-entry"' +
250
                            'size="32"/>' +
251
                    '</div>' +
252
                    '<div class="clearfix"></div>' +
253
                    '{{#mediatype_video}}' +
254
                    '<div class="mb-1">' +
255
                        '<label>{{get_string "size" component}}</label>' +
256
                        '<div class="d-flex flex-wrap align-items-center {{CSS.POSTER_SIZE}}">' +
257
                            '<label class="accesshide">{{get_string "videowidth" component}}</label>' +
258
                            '<input type="text" class="form-control mr-1 {{CSS.WIDTH_INPUT}} input-mini" size="4"/>' +
259
                            ' x ' +
260
                            '<label class="accesshide">{{get_string "videoheight" component}}</label>' +
261
                            '<input type="text" class="form-control ml-1 {{CSS.HEIGHT_INPUT}} input-mini" size="4"/>' +
262
                        '</div>' +
263
                    '</div>' +
264
                    '<div class="clearfix"></div>' +
265
                    '{{renderPartial "form_components.source" context=this id=CSS.POSTER_SOURCE entersourcelabel="poster"}}' +
266
                    '{{/mediatype_video}}' +
267
                '<div>',
268
            ADVANCED_SETTINGS: '' +
269
                '<div class="{{CSS.ADVANCED_SETTINGS}}">' +
270
                    '<div class="form-check">' +
271
                        '<input type="checkbox" checked="true" class="form-check-input {{CSS.MEDIA_CONTROLS_TOGGLE}}"' +
272
                        'id="{{id}}_media-controls-toggle"/>' +
273
                        '<label class="form-check-label" for="{{id}}_media-controls-toggle">' +
274
                        '{{get_string "controls" component}}' +
275
                        '</label>' +
276
                    '</div>' +
277
                    '<div class="form-check">' +
278
                        '<input type="checkbox" class="form-check-input {{CSS.MEDIA_AUTOPLAY_TOGGLE}}"' +
279
                        'id="{{id}}_media-autoplay-toggle"/>' +
280
                        '<label class="form-check-label" for="{{id}}_media-autoplay-toggle">' +
281
                        '{{get_string "autoplay" component}}' +
282
                        '</label>' +
283
                    '</div>' +
284
                    '<div class="form-check">' +
285
                        '<input type="checkbox" class="form-check-input {{CSS.MEDIA_MUTE_TOGGLE}}" ' +
286
                            'id="{{id}}_media-mute-toggle"/>' +
287
                        '<label class="form-check-label" for="{{id}}_media-mute-toggle">' +
288
                        '{{get_string "mute" component}}' +
289
                        '</label>' +
290
                    '</div>' +
291
                    '<div class="form-check">' +
292
                        '<input type="checkbox" class="form-check-input {{CSS.MEDIA_LOOP_TOGGLE}}" ' +
293
                            'id="{{id}}_media-loop-toggle"/>' +
294
                        '<label class="form-check-label" for="{{id}}_media-loop-toggle">' +
295
                        '{{get_string "loop" component}}' +
296
                        '</label>' +
297
                    '</div>' +
298
                '</div>',
299
            TRACK_TABS: '' +
300
                '<ul class="nav nav-tabs mb-3">' +
301
                    '<li data-track-kind="{{CSS.TRACK_SUBTITLES}}" class="nav-item">' +
302
                        '<a class="nav-link active" href="#{{elementid}}_{{id}}_{{CSS.TRACK_SUBTITLES}}"' +
303
                            ' role="tab" data-toggle="tab">' +
304
                            '{{get_string "subtitles" component}}' +
305
                        '</a>' +
306
                    '</li>' +
307
                    '<li data-track-kind="{{CSS.TRACK_CAPTIONS}}" class="nav-item">' +
308
                        '<a class="nav-link" href="#{{elementid}}_{{id}}_{{CSS.TRACK_CAPTIONS}}" role="tab" data-toggle="tab">' +
309
                            '{{get_string "captions" component}}' +
310
                        '</a>' +
311
                    '</li>' +
312
                    '<li data-track-kind="{{CSS.TRACK_DESCRIPTIONS}}"  class="nav-item">' +
313
                        '<a class="nav-link" href="#{{elementid}}_{{id}}_{{CSS.TRACK_DESCRIPTIONS}}"' +
314
                            ' role="tab" data-toggle="tab">' +
315
                            '{{get_string "descriptions" component}}' +
316
                        '</a>' +
317
                    '</li>' +
318
                    '<li data-track-kind="{{CSS.TRACK_CHAPTERS}}" class="nav-item">' +
319
                        '<a class="nav-link" href="#{{elementid}}_{{id}}_{{CSS.TRACK_CHAPTERS}}" role="tab" data-toggle="tab">' +
320
                            '{{get_string "chapters" component}}' +
321
                        '</a>' +
322
                    '</li>' +
323
                    '<li data-track-kind="{{CSS.TRACK_METADATA}}" class="nav-item">' +
324
                        '<a class="nav-link" href="#{{elementid}}_{{id}}_{{CSS.TRACK_METADATA}}" role="tab" data-toggle="tab">' +
325
                            '{{get_string "metadata" component}}' +
326
                        '</a>' +
327
                    '</li>' +
328
                '</ul>' +
329
                '<div class="tab-content">' +
330
                    '<div data-track-kind="{{CSS.TRACK_SUBTITLES}}" class="tab-pane active"' +
331
                        ' id="{{elementid}}_{{id}}_{{CSS.TRACK_SUBTITLES}}">' +
332
                        '<div class="trackhelp">{{{helpStrings.subtitles}}}</div>' +
333
                        '{{renderPartial "form_components.track" context=this sourcelabel="subtitlessourcelabel"' +
334
                            ' addcomponentlabel="addsubtitlestrack"}}' +
335
                    '</div>' +
336
                    '<div data-track-kind="{{CSS.TRACK_CAPTIONS}}" class="tab-pane"' +
337
                        ' id="{{elementid}}_{{id}}_{{CSS.TRACK_CAPTIONS}}">' +
338
                        '<div class="trackhelp">{{{helpStrings.captions}}}</div>' +
339
                        '{{renderPartial "form_components.track" context=this sourcelabel="captionssourcelabel"' +
340
                            ' addcomponentlabel="addcaptionstrack"}}' +
341
                    '</div>' +
342
                    '<div data-track-kind="{{CSS.TRACK_DESCRIPTIONS}}" class="tab-pane"' +
343
                        ' id="{{elementid}}_{{id}}_{{CSS.TRACK_DESCRIPTIONS}}">' +
344
                        '<div class="trackhelp">{{{helpStrings.descriptions}}}</div>' +
345
                        '{{renderPartial "form_components.track" context=this sourcelabel="descriptionssourcelabel"' +
346
                            ' addcomponentlabel="adddescriptionstrack"}}' +
347
                    '</div>' +
348
                    '<div data-track-kind="{{CSS.TRACK_CHAPTERS}}" class="tab-pane"' +
349
                        ' id="{{elementid}}_{{id}}_{{CSS.TRACK_CHAPTERS}}">' +
350
                        '<div class="trackhelp">{{{helpStrings.chapters}}}</div>' +
351
                        '{{renderPartial "form_components.track" context=this sourcelabel="chapterssourcelabel"' +
352
                            ' addcomponentlabel="addchapterstrack"}}' +
353
                    '</div>' +
354
                    '<div data-track-kind="{{CSS.TRACK_METADATA}}" class="tab-pane"' +
355
                        ' id="{{elementid}}_{{id}}_{{CSS.TRACK_METADATA}}">' +
356
                        '<div class="trackhelp">{{{helpStrings.metadata}}}</div>' +
357
                        '{{renderPartial "form_components.track" context=this sourcelabel="metadatasourcelabel"' +
358
                            ' addcomponentlabel="addmetadatatrack"}}' +
359
                    '</div>' +
360
                '</div>',
361
            TRACK: '' +
362
                '<div class="mb-1 {{CSS.TRACK}}">' +
363
                    '{{renderPartial "form_components.source" context=this id=CSS.TRACK_SOURCE entersourcelabel=sourcelabel}}' +
364
                    '<div class="mb-3">' +
365
                        '<label class="w-100" for="lang-input">{{get_string "srclang" component}}</label>' +
366
                        '<select id="lang-input" class="custom-select {{CSS.TRACK_LANG_INPUT}}">' +
367
                            '<optgroup label="{{get_string "languagesinstalled" component}}">' +
368
                                '{{#langsinstalled}}' +
369
                                    '<option value="{{code}}" {{#default}}selected="selected"{{/default}}>{{lang}}</option>' +
370
                                '{{/langsinstalled}}' +
371
                            '</optgroup>' +
372
                            '<optgroup label="{{get_string "languagesavailable" component}} ">' +
373
                                '{{#langsavailable}}<option value="{{code}}">{{lang}}</option>{{/langsavailable}}' +
374
                            '</optgroup>' +
375
                        '</select>' +
376
                    '</div>' +
377
                    '<div class="mb-3">' +
378
                        '<label class="w-100" for="track-input">{{get_string "label" component}}</label>' +
379
                        '<input id="track-input" class="form-control {{CSS.TRACK_LABEL_INPUT}}" type="text"/>' +
380
                    '</div>' +
381
                    '<div class="form-check">' +
382
                        '<input type="checkbox" class="form-check-input {{CSS.TRACK_DEFAULT_SELECT}}"/>' +
383
                        '<label class="form-check-label">{{get_string "default" component}}</label>' +
384
                    '</div>' +
385
                    '{{renderPartial "form_components.add_component" context=this label=addcomponentlabel}}' +
386
                '</div>',
387
            SECTION: '' +
388
                '<legend class="d-flex align-items-center px-1">' +
389
                    '<div class="position-relative d-flex ftoggler align-items-center position-relative mr-1">' +
390
                        '<a role="button" data-toggle="collapse" href="#{{id}}" aria-expanded="false"' +
391
                            'aria-controls="{{id}}"' +
392
                            'class="btn btn-icon mr-1 icons-collapse-expand stretched-link fheader collapsed">' +
393
                            '<span class="expanded-icon icon-no-margin p-2" title="{{get_string "collapse" "moodle"}}">' +
394
                                '<i class="icon fa fa-chevron-down fa-fw " aria-hidden="true"></i>' +
395
                            '</span>' +
396
                            '<span class="collapsed-icon icon-no-margin p-2" title="{{get_string "expand" "moodle"}}">' +
397
                                '<span class="dir-rtl-hide">' +
398
                                    '<i class="icon fa fa-chevron-right fa-fw " aria-hidden="true"></i>' +
399
                                '</span>' +
400
                                '<span class="dir-ltr-hide">' +
401
                                    '<i class="icon fa fa-chevron-left fa-fw " aria-hidden="true"></i>' +
402
                                '</span>' +
403
                            '</span>' +
404
                            '<span class="sr-only">{{get_string name component}}</span>' +
405
                        '</a>' +
406
                        '<h3 class="d-flex align-self-stretch align-items-center mb-0" aria-hidden="true">' +
407
                            '{{get_string name component}}' +
408
                        '</h3>' +
409
                        '</div>' +
410
                    '{{#help}}{{{../help}}}{{/help}}' +
411
                '</legend>'
412
        },
413
        HTML_MEDIA: {
414
            VIDEO: '' +
415
                '&nbsp;<video ' +
416
                    '{{#width}}width="{{../width}}" {{/width}}' +
417
                    '{{#height}}height="{{../height}}" {{/height}}' +
418
                    '{{#poster}}poster="{{../poster}}" {{/poster}}' +
419
                    '{{#showControls}}controls="true" {{/showControls}}' +
420
                    '{{#loop}}loop="true" {{/loop}}' +
421
                    '{{#muted}}muted="true" {{/muted}}' +
422
                    '{{#autoplay}}autoplay="true" {{/autoplay}}' +
423
                    '{{#title}}title="{{../title}}" {{/title}}' +
424
                '>' +
425
                    '{{#sources}}<source src="{{source}}">{{/sources}}' +
426
                    '{{#tracks}}' +
427
                        '<track src="{{track}}" kind="{{kind}}" srclang="{{srclang}}" label="{{label}}"' +
428
                            ' {{#defaultTrack}}default="true"{{/defaultTrack}}>' +
429
                    '{{/tracks}}' +
430
                    '{{#description}}{{../description}}{{/description}}' +
431
                '</video>&nbsp',
432
            AUDIO: '' +
433
                '&nbsp;<audio ' +
434
                    '{{#showControls}}controls="true" {{/showControls}}' +
435
                    '{{#loop}}loop="true" {{/loop}}' +
436
                    '{{#muted}}muted="true" {{/muted}}' +
437
                    '{{#autoplay}}autoplay="true" {{/autoplay}}' +
438
                    '{{#title}}title="{{../title}}" {{/title}}' +
439
                '>' +
440
                    '{{#sources}}<source src="{{source}}">{{/sources}}' +
441
                    '{{#tracks}}' +
442
                        '<track src="{{track}}" kind="{{kind}}" srclang="{{srclang}}" label="{{label}}"' +
443
                            ' {{#defaultTrack}}default="true"{{/defaultTrack}}>' +
444
                    '{{/tracks}}' +
445
                    '{{#description}}{{../description}}{{/description}}' +
446
                '</audio>&nbsp',
447
            LINK: '' +
448
                '<a href="{{url}}" ' +
449
                    '{{#width}}data-width="{{../width}}" {{/width}}' +
450
                    '{{#height}}data-height="{{../height}}"{{/height}}' +
451
                '>{{#name}}{{../name}}{{/name}}{{^name}}{{../url}}{{/name}}</a>'
452
         }
453
    };
454
 
455
Y.namespace('M.atto_media').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
456
 
457
    initializer: function() {
458
        if (this.get('host').canShowFilepicker('media')) {
459
            this.editor.delegate('dblclick', this._displayDialogue, 'video', this);
460
            this.editor.delegate('click', this._handleClick, 'video', this);
461
 
462
            // For some reason, clicking the video on Firefox does not trigger the click event, while in Chrome it does.
463
            // We also need to handle the play/pause instead.
464
            this._attachPlayPauseEvents();
465
            var changeHandler = this._attachPlayPauseEvents.bind(this);
466
            this.get('host').on('change', changeHandler, null);
467
 
468
            this.addButton({
469
                icon: 'e/insert_edit_video',
470
                callback: this._displayDialogue,
471
                tags: 'video, audio',
472
                tagMatchRequiresAll: false
473
            });
474
        }
475
    },
476
 
477
    /**
478
     * Attaches Play/Pause events to the video nodes.
479
     *
480
     * @private
481
     */
482
    _attachPlayPauseEvents: function() {
483
        if (this._handlePlayEndBound === undefined) {
484
            this._handlePlayEndBound = this._handlePlayEnd.bind(this);
485
        }
486
        var videos = this.editor.getDOMNode().querySelectorAll('video');
487
        videos.forEach(function(video) {
488
            // Prevent duplicated event listeners.
489
            video.removeEventListener('play', this._handlePlayEndBound);
490
            video.removeEventListener('pause', this._handlePlayEndBound);
491
            // Add event listeners.
492
            video.addEventListener('play', this._handlePlayEndBound);
493
            video.addEventListener('pause', this._handlePlayEndBound);
494
        }.bind(this));
495
    },
496
 
497
    /**
498
     * Gets the root context for all templates, with extra supplied context.
499
     *
500
     * @method _getContext
501
     * @param  {Object} extra The extra context to add
502
     * @return {Object}
503
     * @private
504
     */
505
    _getContext: function(extra) {
506
        return Y.merge({
507
            elementid: this.get('host').get('elementid'),
508
            component: COMPONENTNAME,
509
            langsinstalled: this.get('langs').installed,
510
            langsavailable: this.get('langs').available,
511
            helpStrings: this.get('help'),
512
            CSS: CSS
513
        }, extra);
514
    },
515
 
516
    /**
517
     * Handles a click on a media element.
518
     *
519
     * @method _handleClick
520
     * @param  {EventFacade} e
521
     * @private
522
     */
523
    _handleClick: function(e) {
524
        var medium = e.target;
525
 
526
        var selection = this.get('host').getSelectionFromNode(medium);
527
        if (this.get('host').getSelection() !== selection) {
528
            this.get('host').setSelection(selection);
529
        }
530
    },
531
 
532
    /**
533
     * Handles a play/end on a media element.
534
     *
535
     * @method _handlePlayEnd
536
     * @param {Event} e
537
     * @private
538
     */
539
    _handlePlayEnd: function(e) {
540
        var medium = Y.one(e.target);
541
 
542
        var selection = this.get('host').getSelectionFromNode(medium);
543
        if (this.get('host').getSelection() !== selection) {
544
            this.get('host').setSelection(selection);
545
            this.get('host')._hasSelectionChanged(e);
546
        }
547
    },
548
 
549
    /**
550
     * Display the media editing tool.
551
     *
552
     * @method _displayDialogue
553
     * @private
554
     */
555
    _displayDialogue: function() {
556
        if (this.get('host').getSelection() === false) {
557
            return;
558
        }
559
 
560
        if (!('renderPartial' in Y.Handlebars.helpers)) {
561
            (function smashPartials(chain, obj) {
562
                Y.each(obj, function(value, index) {
563
                    chain.push(index);
564
                    if (typeof value !== "object") {
565
                        Y.Handlebars.registerPartial(chain.join('.').toLowerCase(), value);
566
                    } else {
567
                        smashPartials(chain, value);
568
                    }
569
                    chain.pop();
570
                });
571
            })([], TEMPLATES);
572
 
573
            Y.Handlebars.registerHelper('renderPartial', function(partialName, options) {
574
                if (!partialName) {
575
                    return '';
576
                }
577
 
578
                var partial = Y.Handlebars.partials[partialName];
579
                var parentContext = options.hash.context ? Y.clone(options.hash.context) : {};
580
                var context = Y.merge(parentContext, options.hash);
581
                delete context.context;
582
 
583
                if (!partial) {
584
                    return '';
585
                }
586
                return new Y.Handlebars.SafeString(Y.Handlebars.compile(partial)(context));
587
            });
588
        }
589
 
590
        var dialogue = this.getDialogue({
591
            headerContent: M.util.get_string('createmedia', COMPONENTNAME),
592
            focusAfterHide: true,
593
            width: 660,
594
            focusOnShowSelector: SELECTORS.URL_INPUT
595
        });
596
 
597
        // Set the dialogue content, and then show the dialogue.
598
        dialogue.set('bodyContent', this._getDialogueContent(this.get('host').getSelection())).show();
599
        M.form.shortforms({formid: this.get('host').get('elementid') + '_atto_media_form'});
600
    },
601
 
602
    /**
603
     * Returns the dialogue content for the tool.
604
     *
605
     * @method _getDialogueContent
606
     * @param  {WrappedRange[]} selection Current editor selection
607
     * @return {Y.Node}
608
     * @private
609
     */
610
    _getDialogueContent: function(selection) {
611
        var content = Y.Node.create(
612
            Y.Handlebars.compile(TEMPLATES.ROOT)(this._getContext())
613
        );
614
 
615
        var medium = this.get('host').getSelectedNodes().filter('video,audio').shift();
616
        var mediumProperties = medium ? this._getMediumProperties(medium) : false;
617
        return this._attachEvents(this._applyMediumProperties(content, mediumProperties), selection);
618
    },
619
 
620
    /**
621
     * Attaches required events to the content node.
622
     *
623
     * @method _attachEvents
624
     * @param  {Y.Node}         content The content to which events will be attached
625
     * @param  {WrappedRange[]} selection Current editor selection
626
     * @return {Y.Node}
627
     * @private
628
     */
629
    _attachEvents: function(content, selection) {
630
        // Delegate add component link for media source fields.
631
        content.delegate('click', function(e) {
632
            e.preventDefault();
633
            this._addMediaSourceComponent(e.currentTarget);
634
        }, SELECTORS.MEDIA_SOURCE + ' .addcomponent', this);
635
 
636
        // Delegate add component link for track fields.
637
        content.delegate('click', function(e) {
638
            e.preventDefault();
639
            this._addTrackComponent(e.currentTarget);
640
        }, SELECTORS.TRACK + ' .addcomponent', this);
641
 
642
        // Only allow one track per tab to be selected as "default".
643
        content.delegate('click', function(e) {
644
            var element = e.currentTarget;
645
            if (element.get('checked')) {
646
                var getKind = function(el) {
647
                    return this._getTrackTypeFromTabPane(el.ancestor('.tab-pane'));
648
                }.bind(this);
649
 
650
                element.ancestor('.root.tab-content').all(SELECTORS.TRACK_DEFAULT_SELECT).each(function(select) {
651
                    if (select !== element && getKind(element) === getKind(select)) {
652
                        select.set('checked', false);
653
                    }
654
                });
655
            }
656
        }, SELECTORS.TRACK_DEFAULT_SELECT, this);
657
 
658
        // Set up filepicker click event.
659
        content.delegate('click', function(e) {
660
            var element = e.currentTarget;
661
            var fptype = (element.ancestor(SELECTORS.POSTER_SOURCE) && 'image') ||
662
                    (element.ancestor(SELECTORS.TRACK_SOURCE) && 'subtitle') ||
663
                    'media';
664
            e.preventDefault();
665
            this.get('host').showFilepicker(fptype, this._getFilepickerCallback(element, fptype), this);
666
        }, '.openmediabrowser', this);
667
 
668
        // This is a nasty hack. Basically we are using BS4 markup for the tabs
669
        // but it isn't completely backwards compatible with BS2. The main problem is
670
        // that the "active" class goes on a different node. So the idea is to put it
671
        // the node for BS4, and then use CSS to make it look right in BS2. However,
672
        // once another tab is clicked, everything sorts itself out, more or less. Except
673
        // that the original "active" tab hasn't had the BS4 "active" class removed
674
        // (so the styles will still apply to it). So we need to remove the "active"
675
        // class on the BS4 node so that BS2 is happy.
676
        //
677
        // This doesn't upset BS4 since it removes this class anyway when clicking on
678
        // another tab.
679
        content.all('.nav-item').on('click', function(elem) {
680
            elem.currentTarget.get('parentNode').all('.active').removeClass('active');
681
        });
682
 
683
        content.one('.submit').on('click', function(e) {
684
            e.preventDefault();
685
            var mediaHTML = this._getMediaHTML(e.currentTarget.ancestor('.atto_form')),
686
                host = this.get('host');
687
            this.getDialogue({
688
                focusAfterHide: null
689
            }).hide();
690
            if (mediaHTML) {
691
                host.setSelection(selection);
692
                host.insertContentAtFocusPoint(mediaHTML);
693
                this.markUpdated();
694
            }
695
        }, this);
696
 
697
        return content;
698
    },
699
 
700
    /**
701
     * Applies medium properties to the content node.
702
     *
703
     * @method _applyMediumProperties
704
     * @param  {Y.Node} content The content to apply the properties to
705
     * @param  {object} properties The medium properties to apply
706
     * @return {Y.Node}
707
     * @private
708
     */
709
    _applyMediumProperties: function(content, properties) {
710
        if (!properties) {
711
            return content;
712
        }
713
 
714
        var applyTrackProperties = function(track, properties) {
715
            track.one(SELECTORS.TRACK_SOURCE + ' ' + SELECTORS.URL_INPUT).set('value', properties.src);
716
            track.one(SELECTORS.TRACK_LANG_INPUT).set('value', properties.srclang);
717
            track.one(SELECTORS.TRACK_LABEL_INPUT).set('value', properties.label);
718
            track.one(SELECTORS.TRACK_DEFAULT_SELECT).set('checked', properties.defaultTrack);
719
        };
720
 
721
        var tabPane = content.one('.root.tab-content > .tab-pane#' + this.get('host').get('elementid') +
722
                              '_' + properties.type.toLowerCase());
723
 
724
        // Populate sources.
725
        tabPane.one(SELECTORS.MEDIA_SOURCE + ' ' + SELECTORS.URL_INPUT).set('value', properties.sources[0]);
726
        Y.Array.each(properties.sources.slice(1), function(source) {
727
            this._addMediaSourceComponent(tabPane.one(SELECTORS.MEDIA_SOURCE + ' .addcomponent'), function(newComponent) {
728
                newComponent.one(SELECTORS.URL_INPUT).set('value', source);
729
            });
730
        }, this);
731
 
732
        // Populate tracks.
733
        Y.Object.each(properties.tracks, function(value, key) {
734
            var trackData = value.length ? value : [{src: '', srclang: '', label: '', defaultTrack: false}];
735
            var paneSelector = SELECTORS['TRACK_' + key.toUpperCase() + '_PANE'];
736
 
737
            applyTrackProperties(tabPane.one(paneSelector + ' ' + SELECTORS.TRACK), trackData[0]);
738
            Y.Array.each(trackData.slice(1), function(track) {
739
                this._addTrackComponent(
740
                    tabPane.one(paneSelector + ' ' + SELECTORS.TRACK + ' .addcomponent'), function(newComponent) {
741
                    applyTrackProperties(newComponent, track);
742
                });
743
            }, this);
744
        }, this);
745
 
746
        // Populate values.
747
        tabPane.one(SELECTORS.TITLE_INPUT).set('value', properties.title);
748
        tabPane.one(SELECTORS.MEDIA_CONTROLS_TOGGLE).set('checked', properties.controls);
749
        tabPane.one(SELECTORS.MEDIA_AUTOPLAY_TOGGLE).set('checked', properties.autoplay);
750
        tabPane.one(SELECTORS.MEDIA_MUTE_TOGGLE).set('checked', properties.muted);
751
        tabPane.one(SELECTORS.MEDIA_LOOP_TOGGLE).set('checked', properties.loop);
752
 
753
        // Determine medium type.
754
        var mediumType = this._getMediumTypeFromTabPane(tabPane);
755
 
756
        if (mediumType === 'video') {
757
            // Populate values unique for video.
758
            tabPane.one(SELECTORS.POSTER_SOURCE + ' ' + SELECTORS.URL_INPUT).setAttribute('value', properties.poster);
759
            tabPane.one(SELECTORS.WIDTH_INPUT).set('value', properties.width);
760
            tabPane.one(SELECTORS.HEIGHT_INPUT).set('value', properties.height);
761
        }
762
 
763
        // Switch to the correct tab.
764
        // Remove active class from all tabs + tab panes.
765
        tabPane.siblings('.active').removeClass('active');
766
        content.all('.root.nav-tabs .nav-item a').removeClass('active');
767
 
768
        // Add active class to the desired tab and tab pane.
769
        tabPane.addClass('active');
770
        content.one(SELECTORS[mediumType.toUpperCase() + '_TAB'] + ' a').addClass('active');
771
 
772
        return content;
773
    },
774
 
775
    /**
776
     * Extracts medium properties.
777
     *
778
     * @method _getMediumProperties
779
     * @param  {Y.Node} medium The medium node from which to extract
780
     * @return {Object}
781
     * @private
782
     */
783
    _getMediumProperties: function(medium) {
784
        var boolAttr = function(elem, attr) {
785
            // As explained in MDL-64175, some OS (like Ubuntu), are removing the value for these attributes.
786
            // So in order to check if attr="true", we need to check if the attribute exists and if the value is empty or true.
787
            return (elem.hasAttribute(attr) && (elem.getAttribute(attr) || elem.getAttribute(attr) === ''));
788
        };
789
 
790
        var tracks = {
791
            subtitles: [],
792
            captions: [],
793
            descriptions: [],
794
            chapters: [],
795
            metadata: []
796
        };
797
 
798
        medium.all('track').each(function(track) {
799
            tracks[track.getAttribute('kind')].push({
800
                src: track.getAttribute('src'),
801
                srclang: track.getAttribute('srclang'),
802
                label: track.getAttribute('label'),
803
                defaultTrack: boolAttr(track, 'default')
804
            });
805
        });
806
 
807
        return {
808
            type: medium.test('video') ? MEDIA_TYPES.VIDEO : MEDIA_TYPES.AUDIO,
809
            sources: medium.all('source').get('src'),
810
            poster: medium.getAttribute('poster'),
811
            title: medium.getAttribute('title'),
812
            width: medium.getAttribute('width'),
813
            height: medium.getAttribute('height'),
814
            autoplay: boolAttr(medium, 'autoplay'),
815
            loop: boolAttr(medium, 'loop'),
816
            muted: boolAttr(medium, 'muted'),
817
            controls: boolAttr(medium, 'controls'),
818
            tracks: tracks
819
        };
820
    },
821
 
822
    /**
823
     * Adds a track form component.
824
     *
825
     * @method _addTrackComponent
826
     * @param  {Y.Node}   element    The element which was used to trigger this function
827
     * @param  {Function} [callback] Function to be called when the new component is added
828
     *     @param {Y.Node}    callback.newComponent The compiled component
829
     * @private
830
     */
831
    _addTrackComponent: function(element, callback) {
832
        var trackType = this._getTrackTypeFromTabPane(element.ancestor('.tab-pane'));
833
        var context = this._getContext({
834
            sourcelabel: trackType + 'sourcelabel',
835
            addcomponentlabel: 'add' + trackType + 'track'
836
        });
837
 
838
        this._addComponent(element, TEMPLATES.FORM_COMPONENTS.TRACK, SELECTORS.TRACK, context, callback);
839
    },
840
 
841
    /**
842
     * Adds a media source form component.
843
     *
844
     * @method _addMediaSourceComponent
845
     * @param  {Y.Node}   element    The element which was used to trigger this function
846
     * @param  {Function} [callback] Function to be called when the new component is added
847
     *     @param {Y.Node}    callback.newComponent The compiled component
848
     * @private
849
     */
850
    _addMediaSourceComponent: function(element, callback) {
851
        var mediumType = this._getMediumTypeFromTabPane(element.ancestor('.tab-pane'));
852
        var context = this._getContext({
853
            multisource: true,
854
            id: CSS.MEDIA_SOURCE,
855
            entersourcelabel: mediumType + 'sourcelabel',
856
            addcomponentlabel: 'addsource',
857
            addsourcehelp: this.get('help').addsource
858
        });
859
        this._addComponent(element, TEMPLATES.FORM_COMPONENTS.SOURCE, SELECTORS.MEDIA_SOURCE, context, callback);
860
    },
861
 
862
    /**
863
     * Adds an arbitrary form component.
864
     *
865
     * This function Compiles and adds the provided component in the supplied 'ancestor' container.
866
     * It will also add links to add/remove the relevant components, attaching the
867
     * necessary events.
868
     *
869
     * @method _addComponent
870
     * @param  {Y.Node}   element    The element which was used to trigger this function
871
     * @param  {String}   component  The component to compile and add
872
     * @param  {String}   ancestor   A selector used to find an ancestor of 'component', to which
873
     *                               the compiled component will be appended
874
     * @param  {Object}   context    The context with which to render the component
875
     * @param  {Function} [callback] Function to be called when the new component is added
876
     *     @param {Y.Node}    callback.newComponent The compiled component
877
     * @private
878
     */
879
    _addComponent: function(element, component, ancestor, context, callback) {
880
        var currentComponent = element.ancestor(ancestor),
881
            newComponent = Y.Node.create(Y.Handlebars.compile(component)(context)),
882
            removeNodeContext = this._getContext(context);
883
 
884
        removeNodeContext.label = "remove";
885
        var removeNode = Y.Node.create(Y.Handlebars.compile(TEMPLATES.FORM_COMPONENTS.REMOVE_COMPONENT)(removeNodeContext));
886
 
887
        removeNode.one('.removecomponent').on('click', function(e) {
888
            e.preventDefault();
889
            currentComponent.remove(true);
890
        });
891
 
892
        currentComponent.insert(newComponent, 'after');
893
        element.ancestor().insert(removeNode, 'after');
894
        element.ancestor().remove(true);
895
 
896
        if (callback) {
897
            callback.call(this, newComponent);
898
        }
899
    },
900
 
901
    /**
902
     * Returns the callback for the file picker to call after a file has been selected.
903
     *
904
     * @method _getFilepickerCallback
905
     * @param  {Y.Node} element The element which triggered the callback
906
     * @param  {String} fptype  The file pickertype (as would be passed to `showFilePicker`)
907
     * @return {Function} The function to be used as a callback when the file picker returns the file
908
     * @private
909
     */
910
    _getFilepickerCallback: function(element, fptype) {
911
        return function(params) {
912
            if (params.url !== '') {
913
                var tabPane = element.ancestor('.tab-pane');
914
                element.ancestor(SELECTORS.SOURCE).one(SELECTORS.URL_INPUT).set('value', params.url);
915
 
916
                // Links (and only links) have a name field.
917
                if (tabPane.get('id') === this.get('host').get('elementid') + '_' + CSS.LINK) {
918
                    tabPane.one(SELECTORS.NAME_INPUT).set('value', params.file);
919
                }
920
 
921
                if (fptype === 'subtitle') {
922
                    var subtitleLang = params.file.split('.vtt')[0].split('-').slice(-1)[0];
923
                    var langObj = this.get('langs').available.reduce(function(carry, lang) {
924
                        return lang.code === subtitleLang ? lang : carry;
925
                    }, false);
926
                    if (langObj) {
927
                        element.ancestor(SELECTORS.TRACK).one(SELECTORS.TRACK_LABEL_INPUT).set('value',
928
                                langObj.lang.substr(0, langObj.lang.lastIndexOf(' ')));
929
                        element.ancestor(SELECTORS.TRACK).one(SELECTORS.TRACK_LANG_INPUT).set('value', langObj.code);
930
                    }
931
                }
932
            }
933
        };
934
    },
935
 
936
    /**
937
     * Given a "medium" tab pane, returns what kind of medium it contains.
938
     *
939
     * @method _getMediumTypeFromTabPane
940
     * @param  {Y.Node} tabPane The tab pane
941
     * @return {String} The type of medium in the pane
942
     */
943
    _getMediumTypeFromTabPane: function(tabPane) {
944
        return tabPane.getAttribute('data-medium-type');
945
    },
946
 
947
    /**
948
     * Given a "track" tab pane, returns what kind of track it contains.
949
     *
950
     * @method _getTrackTypeFromTabPane
951
     * @param  {Y.Node} tabPane The tab pane
952
     * @return {String} The type of track in the pane
953
     */
954
    _getTrackTypeFromTabPane: function(tabPane) {
955
        return tabPane.getAttribute('data-track-kind');
956
    },
957
 
958
    /**
959
     * Returns the HTML to be inserted to the text area.
960
     *
961
     * @method _getMediaHTML
962
     * @param  {Y.Node} form The form from which to extract data
963
     * @return {String} The compiled markup
964
     * @private
965
     */
966
    _getMediaHTML: function(form) {
967
        var mediumType = this._getMediumTypeFromTabPane(form.one('.root.tab-content > .tab-pane.active'));
968
        var tabContent = form.one(SELECTORS[mediumType.toUpperCase() + '_PANE']);
969
 
970
        return this['_getMediaHTML' + mediumType[0].toUpperCase() + mediumType.substr(1)](tabContent);
971
    },
972
 
973
    /**
974
     * Returns the HTML to be inserted to the text area for the link tab.
975
     *
976
     * @method _getMediaHTMLLink
977
     * @param  {Y.Node} tab The tab from which to extract data
978
     * @return {String} The compiled markup
979
     * @private
980
     */
981
    _getMediaHTMLLink: function(tab) {
982
        var context = {
983
            url: tab.one(SELECTORS.URL_INPUT).get('value'),
984
            name: tab.one(SELECTORS.NAME_INPUT).get('value') || false
985
        };
986
 
987
        return context.url ? Y.Handlebars.compile(TEMPLATES.HTML_MEDIA.LINK)(context) : '';
988
    },
989
 
990
    /**
991
     * Returns the HTML to be inserted to the text area for the video tab.
992
     *
993
     * @method _getMediaHTMLVideo
994
     * @param  {Y.Node} tab The tab from which to extract data
995
     * @return {String} The compiled markup
996
     * @private
997
     */
998
    _getMediaHTMLVideo: function(tab) {
999
        var context = this._getContextForMediaHTML(tab);
1000
        context.width = tab.one(SELECTORS.WIDTH_INPUT).get('value') || false;
1001
        context.height = tab.one(SELECTORS.HEIGHT_INPUT).get('value') || false;
1002
        context.poster = tab.one(SELECTORS.POSTER_SOURCE + ' ' + SELECTORS.URL_INPUT).get('value') || false;
1003
 
1004
        return context.sources.length ? Y.Handlebars.compile(TEMPLATES.HTML_MEDIA.VIDEO)(context) : '';
1005
    },
1006
 
1007
    /**
1008
     * Returns the HTML to be inserted to the text area for the audio tab.
1009
     *
1010
     * @method _getMediaHTMLAudio
1011
     * @param  {Y.Node} tab The tab from which to extract data
1012
     * @return {String} The compiled markup
1013
     * @private
1014
     */
1015
    _getMediaHTMLAudio: function(tab) {
1016
        var context = this._getContextForMediaHTML(tab);
1017
 
1018
        return context.sources.length ? Y.Handlebars.compile(TEMPLATES.HTML_MEDIA.AUDIO)(context) : '';
1019
    },
1020
 
1021
    /**
1022
     * Returns the context with which to render a media template.
1023
     *
1024
     * @method _getContextForMediaHTML
1025
     * @param  {Y.Node} tab The tab from which to extract data
1026
     * @return {Object}
1027
     * @private
1028
     */
1029
    _getContextForMediaHTML: function(tab) {
1030
        var tracks = [];
1031
 
1032
        tab.all(SELECTORS.TRACK).each(function(track) {
1033
            tracks.push({
1034
                track: track.one(SELECTORS.TRACK_SOURCE + ' ' + SELECTORS.URL_INPUT).get('value'),
1035
                kind: this._getTrackTypeFromTabPane(track.ancestor('.tab-pane')),
1036
                label: track.one(SELECTORS.TRACK_LABEL_INPUT).get('value') ||
1037
                    track.one(SELECTORS.TRACK_LANG_INPUT).get('value'),
1038
                srclang: track.one(SELECTORS.TRACK_LANG_INPUT).get('value'),
1039
                defaultTrack: track.one(SELECTORS.TRACK_DEFAULT_SELECT).get('checked') ? "true" : null
1040
            });
1041
        }, this);
1042
 
1043
        return {
1044
            sources: tab.all(SELECTORS.MEDIA_SOURCE + ' ' + SELECTORS.URL_INPUT).get('value').filter(function(source) {
1045
                return !!source;
1046
            }).map(function(source) {
1047
                return {source: source};
1048
            }),
1049
            description: tab.one(SELECTORS.MEDIA_SOURCE + ' ' + SELECTORS.URL_INPUT).get('value') || false,
1050
            tracks: tracks.filter(function(track) {
1051
                return !!track.track;
1052
            }),
1053
            showControls: tab.one(SELECTORS.MEDIA_CONTROLS_TOGGLE).get('checked'),
1054
            autoplay: tab.one(SELECTORS.MEDIA_AUTOPLAY_TOGGLE).get('checked'),
1055
            muted: tab.one(SELECTORS.MEDIA_MUTE_TOGGLE).get('checked'),
1056
            loop: tab.one(SELECTORS.MEDIA_LOOP_TOGGLE).get('checked'),
1057
            title: tab.one(SELECTORS.TITLE_INPUT).get('value') || false
1058
        };
1059
    }
1060
}, {
1061
    ATTRS: {
1062
        langs: {},
1063
        help: {}
1064
    }
1065
});
1066
 
1067
 
1068
}, '@VERSION@', {"requires": ["moodle-editor_atto-plugin", "moodle-form-shortforms"]});