Commit | Line | Data |
---|---|---|
adca7326 DW |
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 | ||
62467795 AN |
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 | ||
adca7326 | 24 | /** |
62467795 AN |
25 | * @module moodle-atto_media-button |
26 | */ | |
27 | ||
28 | /** | |
29 | * Atto media selection tool. | |
adca7326 | 30 | * |
62467795 AN |
31 | * @namespace M.atto_media |
32 | * @class Button | |
33 | * @extends M.editor_atto.EditorPlugin | |
adca7326 | 34 | */ |
b269f635 | 35 | |
62467795 | 36 | var COMPONENTNAME = 'atto_media', |
28e93cc4 CB |
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 | }, | |
e5ddec38 | 45 | CSS = { |
28e93cc4 CB |
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', | |
cb694168 | 54 | TITLE_INPUT: 'atto_media_title_entry', |
28e93cc4 CB |
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() | |
e5ddec38 DW |
77 | }, |
78 | SELECTORS = { | |
28e93cc4 CB |
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, | |
cb694168 | 86 | TITLE_INPUT: '.' + CSS.TITLE_INPUT, |
28e93cc4 CB |
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">' + | |
1b217025 | 121 | '<ul class="root nav nav-tabs m-b-1" role="tablist">' + |
28e93cc4 CB |
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/>' + | |
29551c4b | 151 | '<button class="btn btn-secondary submit" type="submit">{{get_string "createmedia" component}}</button>' + |
28e93cc4 CB |
152 | '</div>' + |
153 | '</form>', | |
154 | TAB_PANES: { | |
155 | LINK: '' + | |
156 | '{{renderPartial "form_components.source" context=this id=CSS.LINK_SOURCE}}' + | |
1b217025 BB |
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"/>', | |
28e93cc4 CB |
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 | '<legend class="ftoggler">{{get_string "displayoptions" component}}</legend>' + | |
166 | '<div class="fcontainer">' + | |
cb694168 | 167 | '{{renderPartial "form_components.display_options" context=this id=CSS.VIDEO mediatype_video=true}}' + |
28e93cc4 CB |
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 | '<legend class="ftoggler">{{get_string "advancedsettings" component}}</legend>' + | |
173 | '<div class="fcontainer">' + | |
0030362e | 174 | '{{renderPartial "form_components.advanced_settings" context=this id=CSS.VIDEO}}' + |
28e93cc4 CB |
175 | '</div>' + |
176 | '</fieldset>' + | |
177 | '<fieldset class="collapsible collapsed" id="{{elementid}}_video-tracks">' + | |
178 | '<input name="mform_isexpanded_{{elementid}}_video-tracks" type="hidden">' + | |
179 | '<legend class="ftoggler">{{get_string "tracks" component}} {{{helpStrings.tracks}}}</legend>' + | |
180 | '<div class="fcontainer">' + | |
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}}' + | |
cb694168 RK |
187 | '<fieldset class="collapsible collapsed" id="{{elementid}}_audio-display-options">' + |
188 | '<input name="mform_isexpanded_{{elementid}}_audio-display-options" type="hidden">' + | |
189 | '<legend class="ftoggler">{{get_string "displayoptions" component}}</legend>' + | |
190 | '<div class="fcontainer">' + | |
191 | '{{renderPartial "form_components.display_options" context=this id=CSS.AUDIO}}' + | |
192 | '</div>' + | |
193 | '</fieldset>' + | |
28e93cc4 CB |
194 | '<fieldset class="collapsible collapsed" id="{{elementid}}_audio-advanced-settings">' + |
195 | '<input name="mform_isexpanded_{{elementid}}_audio-advanced-settings" type="hidden">' + | |
196 | '<legend class="ftoggler">{{get_string "advancedsettings" component}}</legend>' + | |
197 | '<div class="fcontainer">' + | |
0030362e | 198 | '{{renderPartial "form_components.advanced_settings" context=this id=CSS.AUDIO}}' + |
28e93cc4 CB |
199 | '</div>' + |
200 | '</fieldset>' + | |
201 | '<fieldset class="collapsible collapsed" id="{{elementid}}_audio-tracks">' + | |
202 | '<input name="mform_isexpanded_{{elementid}}_audio-tracks" type="hidden">' + | |
203 | '<legend class="ftoggler">{{get_string "tracks" component}} {{{helpStrings.tracks}}}</legend>' + | |
204 | '<div class="fcontainer">' + | |
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}}">' + | |
1b217025 BB |
212 | '<div class="m-b-1">' + |
213 | '<label for="url-input">' + | |
28e93cc4 | 214 | '{{#entersourcelabel}}{{get_string ../entersourcelabel ../component}}{{/entersourcelabel}}' + |
1b217025 BB |
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">' + | |
29551c4b | 220 | '<button class="btn btn-secondary openmediabrowser" type="button">' + |
1b217025 BB |
221 | '{{get_string "browserepositories" component}}</button>' + |
222 | '</span>' + | |
223 | '</div>' + | |
224 | '</div>' + | |
28e93cc4 CB |
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}}">' + | |
cb694168 RK |
247 | '<div class="m-b-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}}' + | |
1b217025 BB |
254 | '<div class="m-b-1">' + |
255 | '<label>{{get_string "size" component}}</label>' + | |
256 | '<div class="form-inline {{CSS.POSTER_SIZE}}">' + | |
257 | '<label class="accesshide">{{get_string "videowidth" component}}</label>' + | |
258 | '<input type="text" class="form-control m-r-1 {{CSS.WIDTH_INPUT}} input-mini" size="4"/>' + | |
28e93cc4 | 259 | ' x ' + |
1b217025 BB |
260 | '<label class="accesshide">{{get_string "videoheight" component}}</label>' + |
261 | '<input type="text" class="form-control m-l-1 {{CSS.HEIGHT_INPUT}} input-mini" size="4"/>' + | |
28e93cc4 | 262 | '</div>' + |
1b217025 | 263 | '</div>' + |
28e93cc4 CB |
264 | '<div class="clearfix"></div>' + |
265 | '{{renderPartial "form_components.source" context=this id=CSS.POSTER_SOURCE entersourcelabel="poster"}}' + | |
cb694168 | 266 | '{{/mediatype_video}}' + |
28e93cc4 CB |
267 | '<div>', |
268 | ADVANCED_SETTINGS: '' + | |
269 | '<div class="{{CSS.ADVANCED_SETTINGS}}">' + | |
1b217025 BB |
270 | '<div class="form-check">' + |
271 | '<input type="checkbox" checked="true" class="form-check-input {{CSS.MEDIA_CONTROLS_TOGGLE}}"' + | |
0030362e RK |
272 | 'id="{{id}}_media-controls-toggle"/>' + |
273 | '<label class="form-check-label" for="{{id}}_media-controls-toggle">' + | |
274 | '{{get_string "controls" component}}' + | |
275 | '</label>' + | |
1b217025 BB |
276 | '</div>' + |
277 | '<div class="form-check">' + | |
278 | '<input type="checkbox" class="form-check-input {{CSS.MEDIA_AUTOPLAY_TOGGLE}}"' + | |
0030362e RK |
279 | 'id="{{id}}_media-autoplay-toggle"/>' + |
280 | '<label class="form-check-label" for="{{id}}_media-autoplay-toggle">' + | |
281 | '{{get_string "autoplay" component}}' + | |
282 | '</label>' + | |
1b217025 BB |
283 | '</div>' + |
284 | '<div class="form-check">' + | |
4bc3f503 DM |
285 | '<input type="checkbox" class="form-check-input {{CSS.MEDIA_MUTE_TOGGLE}}" ' + |
286 | 'id="{{id}}_media-mute-toggle"/>' + | |
0030362e RK |
287 | '<label class="form-check-label" for="{{id}}_media-mute-toggle">' + |
288 | '{{get_string "mute" component}}' + | |
289 | '</label>' + | |
1b217025 BB |
290 | '</div>' + |
291 | '<div class="form-check">' + | |
4bc3f503 DM |
292 | '<input type="checkbox" class="form-check-input {{CSS.MEDIA_LOOP_TOGGLE}}" ' + |
293 | 'id="{{id}}_media-loop-toggle"/>' + | |
0030362e RK |
294 | '<label class="form-check-label" for="{{id}}_media-loop-toggle">' + |
295 | '{{get_string "loop" component}}' + | |
296 | '</label>' + | |
1b217025 | 297 | '</div>' + |
28e93cc4 CB |
298 | '</div>', |
299 | TRACK_TABS: '' + | |
1b217025 | 300 | '<ul class="nav nav-tabs mb-3">' + |
28e93cc4 CB |
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: '' + | |
1b217025 | 362 | '<div class="m-b-1 {{CSS.TRACK}}">' + |
28e93cc4 | 363 | '{{renderPartial "form_components.source" context=this id=CSS.TRACK_SOURCE entersourcelabel=sourcelabel}}' + |
1b217025 BB |
364 | '<div class="form-group">' + |
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}}">' + | |
28e93cc4 CB |
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>' + | |
1b217025 BB |
376 | '</div>' + |
377 | '<div class="form-group">' + | |
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>' + | |
28e93cc4 CB |
385 | '{{renderPartial "form_components.add_component" context=this label=addcomponentlabel}}' + |
386 | '</div>' | |
387 | }, | |
388 | HTML_MEDIA: { | |
389 | VIDEO: '' + | |
390 | ' <video ' + | |
391 | '{{#width}}width="{{../width}}" {{/width}}' + | |
392 | '{{#height}}height="{{../height}}" {{/height}}' + | |
393 | '{{#poster}}poster="{{../poster}}" {{/poster}}' + | |
394 | '{{#showControls}}controls="true" {{/showControls}}' + | |
395 | '{{#loop}}loop="true" {{/loop}}' + | |
396 | '{{#muted}}muted="true" {{/muted}}' + | |
397 | '{{#autoplay}}autoplay="true" {{/autoplay}}' + | |
cb694168 | 398 | '{{#title}}title="{{../title}}" {{/title}}' + |
28e93cc4 CB |
399 | '>' + |
400 | '{{#sources}}<source src="{{source}}">{{/sources}}' + | |
401 | '{{#tracks}}' + | |
402 | '<track src="{{track}}" kind="{{kind}}" srclang="{{srclang}}" label="{{label}}"' + | |
403 | ' {{#defaultTrack}}default="true"{{/defaultTrack}}>' + | |
404 | '{{/tracks}}' + | |
405 | '{{#description}}{{../description}}{{/description}}' + | |
406 | '</video> ', | |
407 | AUDIO: '' + | |
408 | ' <audio ' + | |
409 | '{{#showControls}}controls="true" {{/showControls}}' + | |
410 | '{{#loop}}loop="true" {{/loop}}' + | |
411 | '{{#muted}}muted="true" {{/muted}}' + | |
412 | '{{#autoplay}}autoplay="true" {{/autoplay}}' + | |
cb694168 | 413 | '{{#title}}title="{{../title}}" {{/title}}' + |
28e93cc4 CB |
414 | '>' + |
415 | '{{#sources}}<source src="{{source}}">{{/sources}}' + | |
416 | '{{#tracks}}' + | |
417 | '<track src="{{track}}" kind="{{kind}}" srclang="{{srclang}}" label="{{label}}"' + | |
418 | ' {{#defaultTrack}}default="true"{{/defaultTrack}}>' + | |
419 | '{{/tracks}}' + | |
420 | '{{#description}}{{../description}}{{/description}}' + | |
421 | '</audio> ', | |
422 | LINK: '' + | |
423 | '<a href="{{url}}" ' + | |
424 | '{{#width}}data-width="{{../width}}" {{/width}}' + | |
425 | '{{#height}}data-height="{{../height}}"{{/height}}' + | |
426 | '>{{#name}}{{../name}}{{/name}}{{^name}}{{../url}}{{/name}}</a>' | |
427 | } | |
428 | }; | |
62467795 AN |
429 | |
430 | Y.namespace('M.atto_media').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], { | |
431 | ||
28e93cc4 CB |
432 | initializer: function() { |
433 | if (this.get('host').canShowFilepicker('media')) { | |
434 | this.editor.delegate('dblclick', this._displayDialogue, 'video', this); | |
435 | this.editor.delegate('click', this._handleClick, 'video', this); | |
436 | ||
437 | this.addButton({ | |
438 | icon: 'e/insert_edit_video', | |
439 | callback: this._displayDialogue, | |
440 | tags: 'video, audio', | |
441 | tagMatchRequiresAll: false | |
442 | }); | |
443 | } | |
444 | }, | |
445 | ||
62467795 | 446 | /** |
28e93cc4 | 447 | * Gets the root context for all templates, with extra supplied context. |
62467795 | 448 | * |
28e93cc4 CB |
449 | * @method _getContext |
450 | * @param {Object} extra The extra context to add | |
451 | * @return {Object} | |
62467795 AN |
452 | * @private |
453 | */ | |
28e93cc4 CB |
454 | _getContext: function(extra) { |
455 | return Y.merge({ | |
456 | elementid: this.get('host').get('elementid'), | |
457 | component: COMPONENTNAME, | |
458 | langsinstalled: this.get('langs').installed, | |
459 | langsavailable: this.get('langs').available, | |
460 | helpStrings: this.get('help'), | |
461 | CSS: CSS | |
462 | }, extra); | |
463 | }, | |
62467795 AN |
464 | |
465 | /** | |
28e93cc4 | 466 | * Handles a click on a media element. |
62467795 | 467 | * |
28e93cc4 CB |
468 | * @method _handleClick |
469 | * @param {EventFacade} e | |
62467795 AN |
470 | * @private |
471 | */ | |
28e93cc4 CB |
472 | _handleClick: function(e) { |
473 | var medium = e.target; | |
62467795 | 474 | |
28e93cc4 CB |
475 | var selection = this.get('host').getSelectionFromNode(medium); |
476 | if (this.get('host').getSelection() !== selection) { | |
477 | this.get('host').setSelection(selection); | |
6cb48e04 | 478 | } |
62467795 AN |
479 | }, |
480 | ||
481 | /** | |
482 | * Display the media editing tool. | |
483 | * | |
484 | * @method _displayDialogue | |
485 | * @private | |
486 | */ | |
487 | _displayDialogue: function() { | |
28e93cc4 | 488 | if (this.get('host').getSelection() === false) { |
b269f635 DW |
489 | return; |
490 | } | |
62467795 | 491 | |
28e93cc4 CB |
492 | if (!('renderPartial' in Y.Handlebars.helpers)) { |
493 | (function smashPartials(chain, obj) { | |
494 | Y.each(obj, function(value, index) { | |
495 | chain.push(index); | |
496 | if (typeof value !== "object") { | |
497 | Y.Handlebars.registerPartial(chain.join('.').toLowerCase(), value); | |
498 | } else { | |
499 | smashPartials(chain, value); | |
500 | } | |
501 | chain.pop(); | |
502 | }); | |
503 | })([], TEMPLATES); | |
504 | ||
505 | Y.Handlebars.registerHelper('renderPartial', function(partialName, options) { | |
506 | if (!partialName) { | |
507 | return ''; | |
508 | } | |
509 | ||
510 | var partial = Y.Handlebars.partials[partialName]; | |
511 | var parentContext = options.hash.context ? Y.clone(options.hash.context) : {}; | |
512 | var context = Y.merge(parentContext, options.hash); | |
513 | delete context.context; | |
514 | ||
515 | if (!partial) { | |
516 | return ''; | |
517 | } | |
518 | return new Y.Handlebars.SafeString(Y.Handlebars.compile(partial)(context)); | |
519 | }); | |
520 | } | |
521 | ||
62467795 AN |
522 | var dialogue = this.getDialogue({ |
523 | headerContent: M.util.get_string('createmedia', COMPONENTNAME), | |
e5ddec38 | 524 | focusAfterHide: true, |
28e93cc4 CB |
525 | width: 660, |
526 | focusOnShowSelector: SELECTORS.URL_INPUT | |
62467795 AN |
527 | }); |
528 | ||
529 | // Set the dialogue content, and then show the dialogue. | |
28e93cc4 CB |
530 | dialogue.set('bodyContent', this._getDialogueContent(this.get('host').getSelection())).show(); |
531 | M.form.shortforms({formid: this.get('host').get('elementid') + '_atto_media_form'}); | |
adca7326 | 532 | }, |
adca7326 | 533 | |
62467795 | 534 | /** |
28e93cc4 | 535 | * Returns the dialogue content for the tool. |
62467795 AN |
536 | * |
537 | * @method _getDialogueContent | |
28e93cc4 CB |
538 | * @param {WrappedRange[]} selection Current editor selection |
539 | * @return {Y.Node} | |
62467795 AN |
540 | * @private |
541 | */ | |
28e93cc4 CB |
542 | _getDialogueContent: function(selection) { |
543 | var content = Y.Node.create( | |
544 | Y.Handlebars.compile(TEMPLATES.ROOT)(this._getContext()) | |
545 | ); | |
62467795 | 546 | |
28e93cc4 CB |
547 | var medium = this.get('host').getSelectedNodes().filter('video,audio').shift(); |
548 | var mediumProperties = medium ? this._getMediumProperties(medium) : false; | |
549 | return this._attachEvents(this._applyMediumProperties(content, mediumProperties), selection); | |
550 | }, | |
551 | ||
552 | /** | |
553 | * Attaches required events to the content node. | |
554 | * | |
555 | * @method _attachEvents | |
556 | * @param {Y.Node} content The content to which events will be attached | |
557 | * @param {WrappedRange[]} selection Current editor selection | |
558 | * @return {Y.Node} | |
559 | * @private | |
560 | */ | |
561 | _attachEvents: function(content, selection) { | |
562 | // Delegate add component link for media source fields. | |
563 | content.delegate('click', function(e) { | |
564 | e.preventDefault(); | |
565 | this._addMediaSourceComponent(e.currentTarget); | |
566 | }, SELECTORS.MEDIA_SOURCE + ' .addcomponent', this); | |
62467795 | 567 | |
28e93cc4 CB |
568 | // Delegate add component link for track fields. |
569 | content.delegate('click', function(e) { | |
62467795 | 570 | e.preventDefault(); |
28e93cc4 CB |
571 | this._addTrackComponent(e.currentTarget); |
572 | }, SELECTORS.TRACK + ' .addcomponent', this); | |
573 | ||
574 | // Only allow one track per tab to be selected as "default". | |
575 | content.delegate('click', function(e) { | |
576 | var element = e.currentTarget; | |
577 | if (element.get('checked')) { | |
578 | var getKind = function(el) { | |
579 | return this._getTrackTypeFromTabPane(el.ancestor('.tab-pane')); | |
580 | }.bind(this); | |
581 | ||
582 | element.ancestor('.root.tab-content').all(SELECTORS.TRACK_DEFAULT_SELECT).each(function(select) { | |
583 | if (select !== element && getKind(element) === getKind(select)) { | |
584 | select.set('checked', false); | |
585 | } | |
586 | }); | |
587 | } | |
588 | }, SELECTORS.TRACK_DEFAULT_SELECT, this); | |
589 | ||
590 | // Set up filepicker click event. | |
591 | content.delegate('click', function(e) { | |
592 | var element = e.currentTarget; | |
593 | var fptype = (element.ancestor(SELECTORS.POSTER_SOURCE) && 'image') || | |
594 | (element.ancestor(SELECTORS.TRACK_SOURCE) && 'subtitle') || | |
595 | 'media'; | |
596 | e.preventDefault(); | |
597 | this.get('host').showFilepicker(fptype, this._getFilepickerCallback(element, fptype), this); | |
598 | }, '.openmediabrowser', this); | |
599 | ||
600 | // This is a nasty hack. Basically we are using BS4 markup for the tabs | |
601 | // but it isn't completely backwards compatible with BS2. The main problem is | |
602 | // that the "active" class goes on a different node. So the idea is to put it | |
603 | // the node for BS4, and then use CSS to make it look right in BS2. However, | |
604 | // once another tab is clicked, everything sorts itself out, more or less. Except | |
605 | // that the original "active" tab hasn't had the BS4 "active" class removed | |
606 | // (so the styles will still apply to it). So we need to remove the "active" | |
607 | // class on the BS4 node so that BS2 is happy. | |
608 | // | |
609 | // This doesn't upset BS4 since it removes this class anyway when clicking on | |
610 | // another tab. | |
611 | content.all('.nav-item').on('click', function(elem) { | |
612 | elem.currentTarget.get('parentNode').all('.active').removeClass('active'); | |
613 | }); | |
614 | ||
615 | content.one('.submit').on('click', function(e) { | |
616 | e.preventDefault(); | |
617 | var mediaHTML = this._getMediaHTML(e.currentTarget.ancestor('.atto_form')), | |
618 | host = this.get('host'); | |
619 | this.getDialogue({ | |
620 | focusAfterHide: null | |
621 | }).hide(); | |
622 | if (mediaHTML) { | |
623 | host.setSelection(selection); | |
624 | host.insertContentAtFocusPoint(mediaHTML); | |
625 | this.markUpdated(); | |
626 | } | |
62467795 AN |
627 | }, this); |
628 | ||
28e93cc4 | 629 | return content; |
adca7326 | 630 | }, |
62467795 AN |
631 | |
632 | /** | |
28e93cc4 | 633 | * Applies medium properties to the content node. |
62467795 | 634 | * |
28e93cc4 CB |
635 | * @method _applyMediumProperties |
636 | * @param {Y.Node} content The content to apply the properties to | |
637 | * @param {object} properties The medium properties to apply | |
638 | * @return {Y.Node} | |
62467795 AN |
639 | * @private |
640 | */ | |
28e93cc4 CB |
641 | _applyMediumProperties: function(content, properties) { |
642 | if (!properties) { | |
643 | return content; | |
adca7326 | 644 | } |
28e93cc4 CB |
645 | |
646 | var applyTrackProperties = function(track, properties) { | |
647 | track.one(SELECTORS.TRACK_SOURCE + ' ' + SELECTORS.URL_INPUT).set('value', properties.src); | |
648 | track.one(SELECTORS.TRACK_LANG_INPUT).set('value', properties.srclang); | |
649 | track.one(SELECTORS.TRACK_LABEL_INPUT).set('value', properties.label); | |
650 | track.one(SELECTORS.TRACK_DEFAULT_SELECT).set('checked', properties.defaultTrack); | |
651 | }; | |
652 | ||
653 | var tabPane = content.one('.root.tab-content > .tab-pane#' + this.get('host').get('elementid') + | |
654 | '_' + properties.type.toLowerCase()); | |
655 | ||
656 | // Populate sources. | |
657 | tabPane.one(SELECTORS.MEDIA_SOURCE + ' ' + SELECTORS.URL_INPUT).set('value', properties.sources[0]); | |
658 | Y.Array.each(properties.sources.slice(1), function(source) { | |
659 | this._addMediaSourceComponent(tabPane.one(SELECTORS.MEDIA_SOURCE + ' .addcomponent'), function(newComponent) { | |
660 | newComponent.one(SELECTORS.URL_INPUT).set('value', source); | |
661 | }); | |
662 | }, this); | |
663 | ||
664 | // Populate tracks. | |
665 | Y.Object.each(properties.tracks, function(value, key) { | |
666 | var trackData = value.length ? value : [{src: '', srclang: '', label: '', defaultTrack: false}]; | |
667 | var paneSelector = SELECTORS['TRACK_' + key.toUpperCase() + '_PANE']; | |
668 | ||
669 | applyTrackProperties(tabPane.one(paneSelector + ' ' + SELECTORS.TRACK), trackData[0]); | |
670 | Y.Array.each(trackData.slice(1), function(track) { | |
671 | this._addTrackComponent( | |
672 | tabPane.one(paneSelector + ' ' + SELECTORS.TRACK + ' .addcomponent'), function(newComponent) { | |
673 | applyTrackProperties(newComponent, track); | |
674 | }); | |
675 | }, this); | |
676 | }, this); | |
677 | ||
678 | // Populate values. | |
cb694168 | 679 | tabPane.one(SELECTORS.TITLE_INPUT).set('value', properties.title); |
28e93cc4 CB |
680 | tabPane.one(SELECTORS.MEDIA_CONTROLS_TOGGLE).set('checked', properties.controls); |
681 | tabPane.one(SELECTORS.MEDIA_AUTOPLAY_TOGGLE).set('checked', properties.autoplay); | |
682 | tabPane.one(SELECTORS.MEDIA_MUTE_TOGGLE).set('checked', properties.muted); | |
683 | tabPane.one(SELECTORS.MEDIA_LOOP_TOGGLE).set('checked', properties.loop); | |
684 | ||
cb694168 | 685 | // Determine medium type. |
28e93cc4 CB |
686 | var mediumType = this._getMediumTypeFromTabPane(tabPane); |
687 | ||
cb694168 RK |
688 | if (mediumType === 'video') { |
689 | // Populate values unique for video. | |
690 | tabPane.one(SELECTORS.POSTER_SOURCE + ' ' + SELECTORS.URL_INPUT).setAttribute('value', properties.poster); | |
691 | tabPane.one(SELECTORS.WIDTH_INPUT).set('value', properties.width); | |
692 | tabPane.one(SELECTORS.HEIGHT_INPUT).set('value', properties.height); | |
693 | } | |
694 | ||
695 | // Switch to the correct tab. | |
28e93cc4 CB |
696 | // Remove active class from all tabs + tab panes. |
697 | tabPane.siblings('.active').removeClass('active'); | |
698 | content.all('.root.nav-tabs .nav-item a').removeClass('active'); | |
699 | ||
700 | // Add active class to the desired tab and tab pane. | |
701 | tabPane.addClass('active'); | |
702 | content.one(SELECTORS[mediumType.toUpperCase() + '_TAB'] + ' a').addClass('active'); | |
703 | ||
704 | return content; | |
705 | }, | |
706 | ||
707 | /** | |
708 | * Extracts medium properties. | |
709 | * | |
710 | * @method _getMediumProperties | |
711 | * @param {Y.Node} medium The medium node from which to extract | |
712 | * @return {Object} | |
713 | * @private | |
714 | */ | |
715 | _getMediumProperties: function(medium) { | |
716 | var boolAttr = function(elem, attr) { | |
717 | return elem.getAttribute(attr) ? true : false; | |
718 | }; | |
719 | ||
720 | var tracks = { | |
721 | subtitles: [], | |
722 | captions: [], | |
723 | descriptions: [], | |
724 | chapters: [], | |
725 | metadata: [] | |
726 | }; | |
727 | ||
728 | medium.all('track').each(function(track) { | |
729 | tracks[track.getAttribute('kind')].push({ | |
730 | src: track.getAttribute('src'), | |
731 | srclang: track.getAttribute('srclang'), | |
732 | label: track.getAttribute('label'), | |
733 | defaultTrack: boolAttr(track, 'default') | |
734 | }); | |
735 | }); | |
736 | ||
737 | return { | |
738 | type: medium.test('video') ? MEDIA_TYPES.VIDEO : MEDIA_TYPES.AUDIO, | |
739 | sources: medium.all('source').get('src'), | |
740 | poster: medium.getAttribute('poster'), | |
cb694168 | 741 | title: medium.getAttribute('title'), |
28e93cc4 CB |
742 | width: medium.getAttribute('width'), |
743 | height: medium.getAttribute('height'), | |
744 | autoplay: boolAttr(medium, 'autoplay'), | |
745 | loop: boolAttr(medium, 'loop'), | |
746 | muted: boolAttr(medium, 'muted'), | |
747 | controls: boolAttr(medium, 'controls'), | |
748 | tracks: tracks | |
749 | }; | |
750 | }, | |
751 | ||
752 | /** | |
753 | * Adds a track form component. | |
754 | * | |
755 | * @method _addTrackComponent | |
756 | * @param {Y.Node} element The element which was used to trigger this function | |
757 | * @param {Function} [callback] Function to be called when the new component is added | |
758 | * @param {Y.Node} callback.newComponent The compiled component | |
759 | * @private | |
760 | */ | |
761 | _addTrackComponent: function(element, callback) { | |
762 | var trackType = this._getTrackTypeFromTabPane(element.ancestor('.tab-pane')); | |
763 | var context = this._getContext({ | |
764 | sourcelabel: trackType + 'sourcelabel', | |
765 | addcomponentlabel: 'add' + trackType + 'track' | |
766 | }); | |
767 | ||
768 | this._addComponent(element, TEMPLATES.FORM_COMPONENTS.TRACK, SELECTORS.TRACK, context, callback); | |
769 | }, | |
770 | ||
771 | /** | |
772 | * Adds a media source form component. | |
773 | * | |
774 | * @method _addMediaSourceComponent | |
775 | * @param {Y.Node} element The element which was used to trigger this function | |
776 | * @param {Function} [callback] Function to be called when the new component is added | |
777 | * @param {Y.Node} callback.newComponent The compiled component | |
778 | * @private | |
779 | */ | |
780 | _addMediaSourceComponent: function(element, callback) { | |
781 | var mediumType = this._getMediumTypeFromTabPane(element.ancestor('.tab-pane')); | |
782 | var context = this._getContext({ | |
783 | multisource: true, | |
784 | id: CSS.MEDIA_SOURCE, | |
785 | entersourcelabel: mediumType + 'sourcelabel', | |
e24699cc CB |
786 | addcomponentlabel: 'addsource', |
787 | addsourcehelp: this.get('help').addsource | |
28e93cc4 CB |
788 | }); |
789 | this._addComponent(element, TEMPLATES.FORM_COMPONENTS.SOURCE, SELECTORS.MEDIA_SOURCE, context, callback); | |
adca7326 | 790 | }, |
62467795 AN |
791 | |
792 | /** | |
28e93cc4 CB |
793 | * Adds an arbitrary form component. |
794 | * | |
795 | * This function Compiles and adds the provided component in the supplied 'ancestor' container. | |
796 | * It will also add links to add/remove the relevant components, attaching the | |
797 | * necessary events. | |
62467795 | 798 | * |
28e93cc4 CB |
799 | * @method _addComponent |
800 | * @param {Y.Node} element The element which was used to trigger this function | |
801 | * @param {String} component The component to compile and add | |
802 | * @param {String} ancestor A selector used to find an ancestor of 'component', to which | |
803 | * the compiled component will be appended | |
804 | * @param {Object} context The context with which to render the component | |
805 | * @param {Function} [callback] Function to be called when the new component is added | |
806 | * @param {Y.Node} callback.newComponent The compiled component | |
62467795 AN |
807 | * @private |
808 | */ | |
28e93cc4 CB |
809 | _addComponent: function(element, component, ancestor, context, callback) { |
810 | var currentComponent = element.ancestor(ancestor), | |
811 | newComponent = Y.Node.create(Y.Handlebars.compile(component)(context)), | |
812 | removeNodeContext = this._getContext(context); | |
adca7326 | 813 | |
28e93cc4 CB |
814 | removeNodeContext.label = "remove"; |
815 | var removeNode = Y.Node.create(Y.Handlebars.compile(TEMPLATES.FORM_COMPONENTS.REMOVE_COMPONENT)(removeNodeContext)); | |
adca7326 | 816 | |
28e93cc4 CB |
817 | removeNode.one('.removecomponent').on('click', function(e) { |
818 | e.preventDefault(); | |
819 | currentComponent.remove(true); | |
820 | }); | |
adca7326 | 821 | |
28e93cc4 CB |
822 | currentComponent.insert(newComponent, 'after'); |
823 | element.ancestor().insert(removeNode, 'after'); | |
824 | element.ancestor().remove(true); | |
825 | ||
826 | if (callback) { | |
827 | callback.call(this, newComponent); | |
adca7326 | 828 | } |
28e93cc4 CB |
829 | }, |
830 | ||
831 | /** | |
832 | * Returns the callback for the file picker to call after a file has been selected. | |
833 | * | |
834 | * @method _getFilepickerCallback | |
835 | * @param {Y.Node} element The element which triggered the callback | |
836 | * @param {String} fptype The file pickertype (as would be passed to `showFilePicker`) | |
837 | * @return {Function} The function to be used as a callback when the file picker returns the file | |
838 | * @private | |
839 | */ | |
840 | _getFilepickerCallback: function(element, fptype) { | |
841 | return function(params) { | |
842 | if (params.url !== '') { | |
843 | var tabPane = element.ancestor('.tab-pane'); | |
844 | element.ancestor(SELECTORS.SOURCE).one(SELECTORS.URL_INPUT).set('value', params.url); | |
845 | ||
846 | // Links (and only links) have a name field. | |
847 | if (tabPane.get('id') === this.get('host').get('elementid') + '_' + CSS.LINK) { | |
848 | tabPane.one(SELECTORS.NAME_INPUT).set('value', params.file); | |
849 | } | |
850 | ||
851 | if (fptype === 'subtitle') { | |
852 | var subtitleLang = params.file.split('.vtt')[0].split('-').slice(-1)[0]; | |
853 | var langObj = this.get('langs').available.reduce(function(carry, lang) { | |
854 | return lang.code === subtitleLang ? lang : carry; | |
855 | }, false); | |
856 | if (langObj) { | |
857 | element.ancestor(SELECTORS.TRACK).one(SELECTORS.TRACK_LABEL_INPUT).set('value', | |
858 | langObj.lang.substr(0, langObj.lang.lastIndexOf(' '))); | |
859 | element.ancestor(SELECTORS.TRACK).one(SELECTORS.TRACK_LANG_INPUT).set('value', langObj.code); | |
860 | } | |
861 | } | |
862 | } | |
863 | }; | |
864 | }, | |
865 | ||
866 | /** | |
867 | * Given a "medium" tab pane, returns what kind of medium it contains. | |
868 | * | |
869 | * @method _getMediumTypeFromTabPane | |
870 | * @param {Y.Node} tabPane The tab pane | |
871 | * @return {String} The type of medium in the pane | |
872 | */ | |
873 | _getMediumTypeFromTabPane: function(tabPane) { | |
874 | return tabPane.getAttribute('data-medium-type'); | |
875 | }, | |
876 | ||
877 | /** | |
878 | * Given a "track" tab pane, returns what kind of track it contains. | |
879 | * | |
880 | * @method _getTrackTypeFromTabPane | |
881 | * @param {Y.Node} tabPane The tab pane | |
882 | * @return {String} The type of track in the pane | |
883 | */ | |
884 | _getTrackTypeFromTabPane: function(tabPane) { | |
885 | return tabPane.getAttribute('data-track-kind'); | |
886 | }, | |
887 | ||
888 | /** | |
889 | * Returns the HTML to be inserted to the text area. | |
890 | * | |
891 | * @method _getMediaHTML | |
892 | * @param {Y.Node} form The form from which to extract data | |
893 | * @return {String} The compiled markup | |
894 | * @private | |
895 | */ | |
896 | _getMediaHTML: function(form) { | |
897 | var mediumType = this._getMediumTypeFromTabPane(form.one('.root.tab-content > .tab-pane.active')); | |
898 | var tabContent = form.one(SELECTORS[mediumType.toUpperCase() + '_PANE']); | |
899 | ||
900 | return this['_getMediaHTML' + mediumType[0].toUpperCase() + mediumType.substr(1)](tabContent); | |
901 | }, | |
902 | ||
903 | /** | |
904 | * Returns the HTML to be inserted to the text area for the link tab. | |
905 | * | |
906 | * @method _getMediaHTMLLink | |
907 | * @param {Y.Node} tab The tab from which to extract data | |
908 | * @return {String} The compiled markup | |
909 | * @private | |
910 | */ | |
911 | _getMediaHTMLLink: function(tab) { | |
912 | var context = { | |
913 | url: tab.one(SELECTORS.URL_INPUT).get('value'), | |
914 | name: tab.one(SELECTORS.NAME_INPUT).get('value') || false | |
915 | }; | |
916 | ||
917 | return context.url ? Y.Handlebars.compile(TEMPLATES.HTML_MEDIA.LINK)(context) : ''; | |
918 | }, | |
919 | ||
920 | /** | |
921 | * Returns the HTML to be inserted to the text area for the video tab. | |
922 | * | |
923 | * @method _getMediaHTMLVideo | |
924 | * @param {Y.Node} tab The tab from which to extract data | |
925 | * @return {String} The compiled markup | |
926 | * @private | |
927 | */ | |
928 | _getMediaHTMLVideo: function(tab) { | |
929 | var context = this._getContextForMediaHTML(tab); | |
930 | context.width = tab.one(SELECTORS.WIDTH_INPUT).get('value') || false; | |
931 | context.height = tab.one(SELECTORS.HEIGHT_INPUT).get('value') || false; | |
932 | context.poster = tab.one(SELECTORS.POSTER_SOURCE + ' ' + SELECTORS.URL_INPUT).get('value') || false; | |
933 | ||
934 | return context.sources.length ? Y.Handlebars.compile(TEMPLATES.HTML_MEDIA.VIDEO)(context) : ''; | |
935 | }, | |
936 | ||
937 | /** | |
938 | * Returns the HTML to be inserted to the text area for the audio tab. | |
939 | * | |
940 | * @method _getMediaHTMLAudio | |
941 | * @param {Y.Node} tab The tab from which to extract data | |
942 | * @return {String} The compiled markup | |
943 | * @private | |
944 | */ | |
945 | _getMediaHTMLAudio: function(tab) { | |
946 | var context = this._getContextForMediaHTML(tab); | |
947 | ||
948 | return context.sources.length ? Y.Handlebars.compile(TEMPLATES.HTML_MEDIA.AUDIO)(context) : ''; | |
949 | }, | |
950 | ||
951 | /** | |
952 | * Returns the context with which to render a media template. | |
953 | * | |
954 | * @method _getContextForMediaHTML | |
955 | * @param {Y.Node} tab The tab from which to extract data | |
956 | * @return {Object} | |
957 | * @private | |
958 | */ | |
959 | _getContextForMediaHTML: function(tab) { | |
960 | var tracks = []; | |
961 | ||
962 | tab.all(SELECTORS.TRACK).each(function(track) { | |
963 | tracks.push({ | |
964 | track: track.one(SELECTORS.TRACK_SOURCE + ' ' + SELECTORS.URL_INPUT).get('value'), | |
965 | kind: this._getTrackTypeFromTabPane(track.ancestor('.tab-pane')), | |
966 | label: track.one(SELECTORS.TRACK_LABEL_INPUT).get('value') || | |
967 | track.one(SELECTORS.TRACK_LANG_INPUT).get('value'), | |
968 | srclang: track.one(SELECTORS.TRACK_LANG_INPUT).get('value'), | |
969 | defaultTrack: track.one(SELECTORS.TRACK_DEFAULT_SELECT).get('checked') ? "true" : null | |
970 | }); | |
971 | }, this); | |
972 | ||
973 | return { | |
974 | sources: tab.all(SELECTORS.MEDIA_SOURCE + ' ' + SELECTORS.URL_INPUT).get('value').filter(function(source) { | |
975 | return !!source; | |
976 | }).map(function(source) { | |
977 | return {source: source}; | |
978 | }), | |
979 | description: tab.one(SELECTORS.MEDIA_SOURCE + ' ' + SELECTORS.URL_INPUT).get('value') || false, | |
980 | tracks: tracks.filter(function(track) { | |
981 | return !!track.track; | |
982 | }), | |
983 | showControls: tab.one(SELECTORS.MEDIA_CONTROLS_TOGGLE).get('checked'), | |
984 | autoplay: tab.one(SELECTORS.MEDIA_AUTOPLAY_TOGGLE).get('checked'), | |
985 | muted: tab.one(SELECTORS.MEDIA_MUTE_TOGGLE).get('checked'), | |
cb694168 RK |
986 | loop: tab.one(SELECTORS.MEDIA_LOOP_TOGGLE).get('checked'), |
987 | title: tab.one(SELECTORS.TITLE_INPUT).get('value') || false | |
28e93cc4 CB |
988 | }; |
989 | } | |
990 | }, { | |
991 | ATTRS: { | |
992 | langs: {}, | |
993 | help: {} | |
adca7326 | 994 | } |
62467795 | 995 | }); |
adca7326 DW |
996 | |
997 | ||
7d659f21 | 998 | }, '@VERSION@', {"requires": ["moodle-editor_atto-plugin", "moodle-form-shortforms"]}); |