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