main.js 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179
  1. (function($, Drupal, drupalSettings) {
  2. EdlpTheme = function(){
  3. var _ajax_settings = drupalSettings.edlp_ajax;
  4. var _$body = $('body');
  5. var _is_front = _$body.is('.path-frontpage');
  6. var _corpus_ready = false;
  7. var _$corpus_canvas;
  8. var _$row = $('main[role="main"]>.layout-content>.row');
  9. var _$ajaxLinks;
  10. var _audioPlayer;
  11. var _randomPlayer;
  12. var _compoPlayer;
  13. // ___ _ _
  14. // |_ _|_ _ (_) |_
  15. // | || ' \| | _|
  16. // |___|_||_|_|\__|
  17. function init(){
  18. console.log("EdlpTheme init()");
  19. initAjaxLinks();
  20. initHistory();
  21. if(!drupalSettings.path.isFront)
  22. return;
  23. initEvents();
  24. _audioPlayer = new AudioPlayer();
  25. _compoPlayer = new CompoPlayer();
  26. };
  27. // ___ _
  28. // | __|_ _____ _ _| |_ ___
  29. // | _|\ V / -_) ' \ _(_-<
  30. // |___|\_/\___|_||_\__/__/
  31. function initEvents(){
  32. _$body
  33. .on('corpus-map-ready', onCorpusMapReady)
  34. .on('on-studio-chutier-updated', initAjaxLinks)
  35. .on('studio-initialized', function(e){
  36. _compoPlayer.newCompo();
  37. })
  38. .on('studio-not-active', function(e){
  39. _compoPlayer.deactivate();
  40. })
  41. .on('on-studio-compo-updated', function(e){
  42. initAjaxLinks();
  43. _compoPlayer.refresh();
  44. })
  45. .on('on-studio-compo-opened', function(e){
  46. initAjaxLinks();
  47. _compoPlayer.newCompo();
  48. })
  49. .on('search-results-loaded', initAjaxLinks)
  50. // do not close index or notice modale on entree click
  51. .on('open_entree', function(e){
  52. console.log('on_open_entree : e', e);
  53. closeAllModals();
  54. // add body class for currently loaded content
  55. // var body_classes = [
  56. // 'path-'+sys_path.replace(/\//g, '-'),
  57. // 'entity-type-'+data.entity_type,
  58. // 'bundle-'+data.bundle,
  59. // 'view-mode-'+data.view_mode
  60. // ];
  61. _$body.removeClass();//.addClass(body_classes.join(' '));
  62. // record new history state
  63. if(typeof e.url != 'undefined'){
  64. // var state = {
  65. // ajax_path:null,
  66. // sys_path:null,
  67. // entree_tid:e.tid
  68. // };
  69. var state = getSysPathState(e.sys_path);
  70. history.pushState(state, null, e.url);
  71. }
  72. })
  73. .on('close_entree', backToFrontPage);
  74. }
  75. // ___ _ _ ___
  76. // / __| __ _ _ ___| | | _ ) __ _ _ _ ___
  77. // \__ \/ _| '_/ _ \ | | _ \/ _` | '_(_-<
  78. // |___/\__|_| \___/_|_|___/\__,_|_| /__/
  79. function initScrollbars(){
  80. // console.log("initScrollbars");
  81. // TODO: find a better js scroll than overlayScrollbars which does not handle well max-height + overflow-y:auto;
  82. // $('.os-scroll').overlayScrollbars({
  83. // overflowBehavior:{
  84. // x:'h',
  85. // y:'scroll',
  86. // clipAlways:false
  87. // }
  88. // });
  89. };
  90. // _ _
  91. // /_\ (_)__ ___ __
  92. // / _ \ | / _` \ \ /
  93. // /_/ \_\/ \__,_/_\_\
  94. // |__/
  95. function getSysPathState(sys_path, view_mode){
  96. // console.log('Theme : getSysPathState', sys_path);
  97. var state = {
  98. 'sys_path':sys_path,
  99. 'ajax_path': sys_path
  100. };
  101. // convert node link to edlp_ajax_node module links
  102. var node_match = state.ajax_path.match(/^\/?(node\/(\d+))$/i);
  103. console.log('node_match', node_match);
  104. var term_match = state.ajax_path.match(/^\/?(taxonomy\/term\/(\d+))$/i);
  105. console.log('term_match', term_match);
  106. if(node_match){
  107. state.ajax_path = _ajax_settings.entityjson_path+'/'+node_match[1];
  108. state.node_nid = node_match[2];
  109. // check for viewmode attribute
  110. if(view_mode){
  111. state.ajax_path += '/'+view_mode;
  112. }
  113. }else if(term_match){
  114. // terms are always entrees, there's no other vocabulary links in front
  115. state.ajax_path = _ajax_settings.entityjson_path+'/'+term_match[1];
  116. state.ajax_path = state.ajax_path.replace(/taxonomy\/term/, 'taxonomy_term');
  117. state.entree_tid = term_match[2];
  118. // check for viewmode attribute
  119. if(view_mode){
  120. state.ajax_path += '/'+view_mode;
  121. state.view_mode = view_mode;
  122. }else{
  123. state.ajax_path = null;
  124. }
  125. }else{
  126. // convert other link to ajax
  127. // TODO: we assume that other links (no node, no term) are all from own modules (e.g. productions) !! may not be true !!
  128. state.ajax_path += '/ajax'
  129. }
  130. return state;
  131. };
  132. function ajaxLoadContent(state){
  133. console.log('ajaxLoadContent : url', state.url);
  134. _$body.addClass('ajax-loading');
  135. var path = window.location.origin + Drupal.url(state.ajax_path);
  136. $.getJSON(path, {})
  137. .done(function(data){
  138. onAjaxLoaded(data, state);
  139. })
  140. .fail(function(jqxhr, textStatus, error){
  141. onAjaxLoadError(jqxhr, textStatus, error, state.sys_path);
  142. });
  143. };
  144. function onAjaxLoadError(jqxhr, textStatus, error, sys_path){
  145. console.warn('ajaxlink load failed for '+sys_path+' : '+error, jqxhr.responseText);
  146. $('.ajax-loading').removeClass('ajax-loading');
  147. _$body.removeClass('ajax-loading');
  148. };
  149. function onAjaxLoaded(data, state){
  150. console.log('ajax loaded url:'+state.url+' ajax_path:'+state.ajax_path+' sys_path:'+state.sys_path);
  151. // reset all style may been added by other pages (like masonry for productions)
  152. // and replace all content with newly loaded
  153. // TODO: build a system to replace or append contents (like studio + search)
  154. _$row.removeAttr('style').html(data.rendered);
  155. // add body class for currently loaded content
  156. var body_classes = [
  157. 'path-'+state.sys_path.replace(/\//g, '-'),
  158. 'entity-type-'+data.entity_type,
  159. 'bundle-'+data.bundle,
  160. 'view-mode-'+data.view_mode
  161. ];
  162. _$body.removeClass().addClass(body_classes.join(' '));
  163. // id node add a generic path-node class to body
  164. // m = state.sys_path.match(/^\/?(node\/\d+)$/g);
  165. // if(m)
  166. if(state.node_nid)
  167. _$body.addClass('path-edlp-node');
  168. // handle clicked link classes
  169. $('.ajax-loading').removeClass('ajax-loading');
  170. $('.is-active').removeClass('is-active');
  171. $('.is-active-trail').removeClass('is-active-trail');
  172. if(typeof state.selector != 'undefined'){
  173. // in case of entree link (actualy, selector is used only for entries links)
  174. console.log('selector', state.selector);
  175. $('a[selector="'+state.selector+'"]').addClass('is-active');
  176. }else{
  177. $('a[data-drupal-link-system-path="'+state.sys_path+'"]').addClass('is-active');
  178. // as new content is not related to entree, we trigger close entree
  179. _$body.trigger({'type':'new-content-not-entree-ajax-loaded'});
  180. }
  181. // if bundle page (productions) activate production links
  182. if (typeof data.bundle != 'undefined' && data.bundle == "page") {
  183. $('a[data-drupal-link-system-path="productions"]').addClass('is-active-trail');
  184. }
  185. // if node is in production menu tree, set first level of tree active, e.g. pieces sonores
  186. if (typeof data.menu_parents != 'undefined') {
  187. for (var i = 0; i < data.menu_parents.length; i++) {
  188. var menu_sys_path = data.menu_parents[i];
  189. $('a[data-drupal-link-system-path="'+menu_sys_path+'"]').addClass('is-active-trail');
  190. }
  191. }
  192. // if block attached (eg : from edlp_productions module)
  193. // not used anymore as production block is always present (but not visible)
  194. if(typeof data.block != 'undefined'){
  195. // if block not already added
  196. if(!$('#'+data.block.id, '.region-'+data.block.region).length){
  197. $('.region-'+data.block.region).append(data.block.rendered);
  198. }
  199. }
  200. // initScrollbars();
  201. if(state.sys_path == "productions"){
  202. initProductions();
  203. }else{
  204. addCloseModalBtnToCols();
  205. }
  206. initAjaxLinks();
  207. // trigger other modules behaviours
  208. _$body.trigger({'type':'new-content-ajax-loaded'});
  209. // and call druapl behaviours
  210. Drupal.attachBehaviors(_$row[0]);
  211. _$body.attr('booted', 'booted');
  212. _$body.removeClass('ajax-loading');
  213. // url is null means that we are loading content on popState event
  214. // so we don't record the state again
  215. if(state.url){
  216. // var state = {
  217. // ajax_path:ajax_path,
  218. // sys_path:sys_path,
  219. // };
  220. // console.log('url:'+url+' ; state',state);
  221. // console.log(window.location);
  222. // we can not pushestate with absolute url
  223. history.pushState(state, null, state.url);
  224. }
  225. };
  226. function addCloseModalBtnToCols(){
  227. $('.col', _$row).each(function(index, el) {
  228. if($('span.close-col-btn', this).length)
  229. return true;
  230. $(this).children('.wrapper').prepend($('<span>')
  231. .addClass('close-col-btn')
  232. .on('click', function(e){
  233. // check for theme attribute and emmit event
  234. var $col = $(this).parents('.col');
  235. var theme = $col.attr('theme');
  236. if(theme != ''){
  237. _$body.trigger({'type':theme+'-col-closed'});
  238. }
  239. // remove the col
  240. $col.remove();
  241. // if row is empty and we are not in productions call closeAllModals()
  242. if(!$('.col', _$row).length
  243. && !_$body.is('.entity-type-node.bundle-page')){
  244. backToFrontPage();
  245. }
  246. })
  247. );
  248. });
  249. };
  250. // _ _ _ _
  251. // | || (_)__| |_ ___ _ _ _ _
  252. // | __ | (_-< _/ _ \ '_| || |
  253. // |_||_|_/__/\__\___/_| \_, |
  254. // |__/
  255. function initHistory(){
  256. initFirstLoad();
  257. window.addEventListener('popstate', onHistoryPopState);
  258. };
  259. function initFirstLoad(){
  260. console.log('theme : initFirstLoad()');
  261. var origin_sys_path = window.localStorage.getItem('edlp_origin_path');
  262. if(origin_sys_path){
  263. var origin_url = window.localStorage.getItem('edlp_origin_url');
  264. // origin_hash is used as viewmode for taxonomy term entrees load (index or notice)
  265. var origin_hash = window.localStorage.getItem('edlp_origin_hash');
  266. var view_mode = origin_hash.replace('#', '');
  267. if(view_mode){
  268. var $link = $('[href="'+origin_url+'"][viewmode="'+view_mode+'"]');
  269. var selector = $link.attr('selector') || null;
  270. if(selector){
  271. // in case of entree link (actualy, selector is used only for entries links)
  272. if(_corpus_ready){
  273. _$corpus_canvas.trigger({
  274. type:'open-entree',
  275. tid:$link.attr('tid')
  276. });
  277. }else{
  278. // else : EdlpCorpus will check when ready if entry item (notice or index) is already .is-active
  279. // .is-active class is added by onAjaxLoaded() (when content is loaded)
  280. // but what if corpus ready before onAjaxLoaded
  281. $('li.entree[tid="'+$link.attr('tid')+'"] a.term-link').addClass('is-active');
  282. }
  283. }
  284. }
  285. // create history state
  286. var state = getSysPathState(origin_sys_path, view_mode);
  287. // only if not entree path
  288. if(state.ajax_path){
  289. // load content through ajax
  290. // ajaxLoadContent(null, state.sys_path, state.ajax_path, selector);
  291. ajaxLoadContent(state);
  292. }
  293. // TODO what about entree alone (without notice or index)
  294. if(state.entree_tid){
  295. openEntree(state.entree_tid);
  296. }
  297. // record history state
  298. history.replaceState(state, null, origin_url+origin_hash);
  299. // reset the storage
  300. window.localStorage.removeItem("edlp_origin_path");
  301. window.localStorage.removeItem("edlp_origin_url");
  302. }else{
  303. history.replaceState({home:true}, null, window.location.pathname);
  304. _$body.attr('booted', 'booted');
  305. }
  306. };
  307. function onHistoryPopState(e){
  308. console.log('onPopState',e.state);
  309. if(e.state.home){
  310. backToFrontPage(true);
  311. }else{
  312. if(e.state.entree_tid){
  313. openEntree(e.state.entree_tid);
  314. }
  315. if(e.state.ajax_path){
  316. e.state.url = null;
  317. // ajaxLoadContent(null, e.state.sys_path, e.state.ajax_path)
  318. ajaxLoadContent(e.state);
  319. }
  320. }
  321. };
  322. // _ _ _ _ _
  323. // /_\ (_)__ ___ _| | (_)_ _ | |__ ___
  324. // / _ \ | / _` \ \ / |__| | ' \| / /(_-<
  325. // /_/ \_\/ \__,_/_\_\____|_|_||_|_\_\/__/
  326. // |__/
  327. function initAjaxLinks(){
  328. // console.log('initAjaxLinks');
  329. $('a.site-name', '#block-edlptheme-branding')
  330. .add('a', '#block-mainnavigation')
  331. .add('a', '#block-footer.menu--footer')
  332. .add('a', '#block-productions')
  333. .add('a', 'article.node:not(.node--type-enregistrement) h2.node-title')
  334. .add('a', '.productions-subtree')
  335. .add('a', '.productions-parent')
  336. // .add('a.index-link, a.notice-link', '#block-edlpentreesblock')
  337. .addClass('ajax-link');
  338. _$ajaxLinks = $('.ajax-link:not(.ajax-enabled)')
  339. .each(function(i,e){
  340. var $this = $(this);
  341. // avoid already ajaxified links
  342. if($this.is('.ajax-enable')) return;
  343. if($this.attr('data-drupal-link-system-path')){
  344. $this.on('click', onClickAjaxLink).addClass('ajax-enable');
  345. }
  346. });
  347. };
  348. function onClickAjaxLink(e){
  349. e.preventDefault();
  350. var $link = $(this);
  351. if($link.is('.is-active'))
  352. return false;
  353. // Audio links
  354. // launch audio player and stop here
  355. if($link.is('.audio-link')){
  356. _audioPlayer
  357. .emmit('stop-shuffle')
  358. .openDocument({
  359. nid:$link.attr('nid'),
  360. audio_url:$link.attr('audio_url')
  361. });
  362. return false;
  363. }
  364. // other links
  365. var sys_path = $(this).attr('data-drupal-link-system-path');
  366. // front page
  367. // just remove contents and stop here
  368. if(sys_path == '<front>'){
  369. backToFrontPage();
  370. return false;
  371. }
  372. var view_mode = $link.attr('viewmode');
  373. var state = getSysPathState(sys_path, view_mode);
  374. state.url = $(this).attr('href');
  375. if(view_mode){
  376. state.url += "#"+view_mode;
  377. }
  378. if($link.is('[selector]')){
  379. state.selector = $link.attr('selector');
  380. }
  381. $link.addClass('ajax-loading');
  382. // ajaxLoadContent(url, sys_path, state.ajax_path, selector);
  383. ajaxLoadContent(state);
  384. return false;
  385. };
  386. // ___
  387. // / __|___ _ _ _ __ _ _ ___
  388. // | (__/ _ \ '_| '_ \ || (_-<
  389. // \___\___/_| | .__/\_,_/__/
  390. // |_|
  391. function onCorpusMapReady(e){
  392. //console.log('theme : onCorpusReady', e);
  393. _corpus_ready = true;
  394. _$corpus_canvas = $('canvas#corpus-map');
  395. _$corpus_canvas
  396. .on('corpus-cliked-on-map', function(e) {
  397. //console.log('theme : corpus-cliked-on-map');
  398. backToFrontPage();
  399. })
  400. .on('corpus-cliked-on-node', function(e) {
  401. //console.log('theme : corpus-cliked-on-node', e);
  402. _audioPlayer
  403. .emmit('stop-shuffle')
  404. .openDocument(e.target_node);
  405. });
  406. _randomPlayer = new RandomPlayer(e.playlist);
  407. _$body.attr('corpus-map', 'ready');
  408. }
  409. function openEntree(tid){
  410. if(tid){
  411. closeAllModals();
  412. _$body.removeClass();//.addClass(body_classes.join(' '));
  413. // open entree
  414. if(_corpus_ready){
  415. _$corpus_canvas.trigger({
  416. type:'open-entree',
  417. tid:tid
  418. });
  419. }else{
  420. // else : EdlpCorpus will check when ready if entry item (notice or index) is already .is-active
  421. $('li.entree[tid="'+tid+'"] a.term-link').addClass('is-active');
  422. }
  423. }
  424. };
  425. // _ _ _
  426. // /_\ _ _ __| (_)___
  427. // / _ \ || / _` | / _ \
  428. // /_/ \_\_,_\__,_|_\___/
  429. //
  430. // https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement
  431. // https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/samples/gg589528%28v%3dvs.85%29
  432. // https://www.binarytides.com/using-html5-audio-element-javascript/
  433. function AudioPlayer(){
  434. var that = this;
  435. this.fid;
  436. this.audio = new Audio();
  437. // audio events
  438. this.audio_events = ["loadedmetadata","canplay","playing","pause","timeupdate","ended"];
  439. // UI dom objects
  440. this.$container = $('<div id="audio-player">');
  441. // btns
  442. this.$btns = $('<div>').addClass('btns').appendTo(this.$container);
  443. this.$previous = $('<div>').addClass('previous').appendTo(this.$btns);
  444. this.$playpause = $('<div>').addClass('play-pause').appendTo(this.$btns);
  445. this.$next = $('<div>').addClass('next').appendTo(this.$btns);
  446. // timeline
  447. this.$timelinecont= $('<div>').addClass('time-line-container').appendTo(this.$container);
  448. this.$timeline = $('<div>').addClass('time-line').appendTo(this.$timelinecont);
  449. this.$loader = $('<div>').addClass('loader').appendTo(this.$timeline);
  450. this.$cursor = $('<div>').addClass('cursor').appendTo(this.$timeline);
  451. // time
  452. this.$time = $('<div>').addClass('time').appendTo(this.$container);
  453. this.$currentTime = $('<div>').addClass('current-time').html('00:00').appendTo(this.$time);
  454. this.$duration = $('<div>').addClass('duration').html('00:00').appendTo(this.$time);
  455. // favoris
  456. this.$fav = $('<div>').addClass('favoris').appendTo(this.$container);
  457. // cartel
  458. this.$cartel = $('<div>').addClass('cartel').appendTo(this.$container);
  459. // hiding
  460. this.hideTimer = false;
  461. this.hideTimeMS = 10000;
  462. // history
  463. this.currentHistoricIndex = null;
  464. this.historic = [];
  465. this.shuffle_is_active = false;
  466. // object events
  467. this.event_handlers = {
  468. 'audio-open-document':[],
  469. 'audio-play':[],
  470. 'audio-pause':[],
  471. 'audio-play-next':[],
  472. 'audio-ended':[],
  473. 'stop-shuffle':[]
  474. };
  475. this.init();
  476. };
  477. AudioPlayer.prototype = {
  478. init(){
  479. // append ui to document
  480. this.$container.appendTo('header[role="banner"] .region-header');
  481. // record timeline width
  482. this.timeline_w = parseInt(this.$timeline.width());
  483. // init audio events
  484. var fn = '';
  485. for (var i = 0; i < this.audio_events.length; i++) {
  486. fn = this.audio_events[i];
  487. // capitalize first letter of event (only cosmetic :p )
  488. fn = 'on'+fn.charAt(0).toUpperCase()+fn.slice(1);
  489. this.audio.addEventListener(
  490. this.audio_events[i],
  491. this[fn].bind(this),
  492. true);
  493. }
  494. // init btns events
  495. this.$previous.on('click', this.playPrevious.bind(this));
  496. this.$playpause.on('click', this.togglePlayPause.bind(this));
  497. this.$next.on('click', this.playNext.bind(this));
  498. },
  499. openDocument(node, caller){
  500. // console.log('AudioPlayer openDocument', node);
  501. if(typeof node == 'undefined'
  502. || typeof node.nid == 'undefined'
  503. || typeof node.audio_url == 'undfined'){
  504. console.warn('AudioPlayer openDocument() node is malformed', node);
  505. return false;
  506. }
  507. this.historic.push(node);
  508. this.currentHistoricIndex = this.historic.length-1;
  509. // this.shuffle_mode = shuffle_mode || false;
  510. // TODO: add an hash tag to be able to share and play audio from any where
  511. this.emmit('audio-open-document', {caller:caller});
  512. this.launch();
  513. },
  514. launch(){
  515. this.clearTimeOutToHide();
  516. this.setSRC(this.historic[this.currentHistoricIndex].audio_url);
  517. this.loadNode(this.historic[this.currentHistoricIndex].nid);
  518. // emmit new playing doc (e.g.: corpus map nowing that audio played from RandomPlayer)
  519. try {
  520. _$corpus_canvas.trigger({
  521. 'type':'audio-node-opened',
  522. 'nid':this.historic[this.currentHistoricIndex].nid
  523. });
  524. } catch (e) {
  525. console.info('AudioPlayer : _$corpus_canvas does not exists');
  526. }
  527. this.showHidePreviousBtn();
  528. this.showHideNextBtn();
  529. this.show();
  530. },
  531. // audio functions
  532. setSRC(url){
  533. // console.log('AudioPlayer setSRC : url', url);
  534. this.audio.src = url;
  535. },
  536. onLoadedmetadata(){
  537. var rem = parseInt(this.audio.duration, 10),
  538. mins = Math.floor(rem/60,10),
  539. secs = rem - mins*60;
  540. this.$duration.html('<span>'+(mins<10 ? '0':'')+mins+':'+(secs<10 ? '0':'')+secs+'</span>');
  541. this.updateLoadingBar();
  542. },
  543. updateLoadingBar(){
  544. this.$loader.css({
  545. 'width':parseInt((100 * this.audio.buffered.end(0) / this.audio.duration), 10)+'%'
  546. });
  547. if( this.audio.buffered.end(0) < this.audio.duration ){
  548. // loop through this function until file is fully loaded
  549. var that = this;
  550. window.requestAnimationFrame(that.updateLoadingBar.bind(that));
  551. }else{
  552. //console.log('Audio fully loaded');
  553. }
  554. },
  555. onCanplay(){
  556. this.play();
  557. },
  558. play(){
  559. this.clearTimeOutToHide();
  560. this.audio.play();
  561. },
  562. playPrevious(){
  563. if(this.currentHistoricIndex > 0){
  564. this.currentHistoricIndex -= 1;
  565. this.launch();
  566. }
  567. },
  568. playNext(){
  569. if(this.currentHistoricIndex < this.historic.length-1){
  570. this.currentHistoricIndex += 1;
  571. this.launch();
  572. }else{
  573. this.emmit('audio-play-next');
  574. }
  575. },
  576. togglePlayPause(e){
  577. if(this.audio.paused){
  578. this.audio.play();
  579. }else{
  580. this.audio.pause();
  581. }
  582. },
  583. stop(){
  584. // console.log('AudioPlayer stop()');
  585. this.audio.pause();
  586. this.timeOutToHide();
  587. },
  588. // audio events
  589. onPlaying(){
  590. this.$btns.addClass('is-playing');
  591. this.emmit('audio-play');
  592. },
  593. onPause(){
  594. this.$btns.removeClass('is-playing');
  595. this.emmit('audio-pause');
  596. },
  597. onTimeupdate(){
  598. // move cursor
  599. this.$cursor.css({
  600. 'left':(this.audio.currentTime/this.audio.duration * this.timeline_w)+"px"
  601. });
  602. // update time text display
  603. var rem = parseInt(this.audio.currentTime, 10),
  604. mins = Math.floor(rem/60,10),
  605. secs = rem - mins*60;
  606. this.$currentTime.html('<span>'+(mins<10 ? '0':'')+mins+':'+(secs<10 ? '0':'')+secs+'</span>');
  607. },
  608. onEnded(){
  609. this.emmit('audio-ended');
  610. this.stop();
  611. },
  612. // cartel functions
  613. loadNode(nid){
  614. this.$cartel.addClass('loading');
  615. var vm = 'player_cartel';
  616. var ajax_path = _ajax_settings.entityjson_path+'/node/'+nid+'/'+vm;
  617. var path = window.location.origin + Drupal.url(ajax_path);
  618. $.getJSON(path, {})
  619. .done(this.onNodeLoaded.bind(this))
  620. .fail(this.onNodeLoadFail.bind(this));
  621. },
  622. onNodeLoaded(data){
  623. // console.log('AudioPlayer node loaded');
  624. this.$cartel.html(data.rendered).removeClass('loading');
  625. _$body.trigger({'type':'new-audio-cartel-loaded'});
  626. initAjaxLinks();
  627. },
  628. onNodeLoadFail(jqxhr, textStatus, error){
  629. console.warn('AudioPlayer node load failed', jqxhr.responseText);
  630. this.$cartel.removeClass('loading').html('');
  631. },
  632. // global
  633. show(){
  634. this.$container.addClass('visible');
  635. },
  636. showHidePreviousBtn(){
  637. if(this.historic.length > 1 && this.currentHistoricIndex > 0){
  638. this.$previous.addClass('is-active');
  639. }else{
  640. this.$previous.removeClass('is-active');
  641. }
  642. },
  643. showHideNextBtn(){
  644. if(this.currentHistoricIndex < this.historic.length-1 || this.shuffle_is_active){
  645. this.$next.addClass('is-active');
  646. }else{
  647. this.$next.removeClass('is-active');
  648. }
  649. },
  650. timeOutToHide(){
  651. // console.log('AudioPlayer timeOutToHide()');
  652. this.clearTimeOutToHide();
  653. this.hideTimer = setTimeout(this.hide.bind(this), this.hideTimeMS);
  654. },
  655. clearTimeOutToHide(){
  656. // console.log('AudioPlayer clearTimeOutToHide()',this.hideTimer);
  657. if(this.hideTimer){
  658. clearTimeout(this.hideTimer);
  659. this.hideTimer = false;
  660. }
  661. },
  662. hide(){
  663. // console.log('AudioPlayer hide()');
  664. this.$container.removeClass('visible');
  665. // trigger highlighted node remove on corpus map
  666. try {
  667. _$corpus_canvas.trigger('audio-node-closed');
  668. } catch (e) {
  669. console.info('AudioPlayer hide() : _$corpus_canvas does not exists');
  670. }
  671. },
  672. // object events
  673. on(event_name, handler){
  674. if(typeof this.event_handlers[event_name] == 'undefined'){
  675. console.warn('AudioPlayer : event '+event_name+' does not exists');
  676. }
  677. this.event_handlers[event_name].push(handler);
  678. return this;
  679. },
  680. emmit(event_name, args){
  681. // console.log('AudioPlayer emmit() event_name', event_name);
  682. // console.log('AudioPlayer emmit() handlers', this.event_handlers[event_name]);
  683. var handler;
  684. var args = args || {};
  685. for (var i = this.event_handlers[event_name].length-1; i >= 0 ; i--) {
  686. handler = this.event_handlers[event_name][i];
  687. // console.log('AudioPlayer emmit() loop handler', handler);
  688. setTimeout(function(){
  689. // console.log('AudioPlayer emmit() timeout handler', handler);
  690. handler(args);
  691. }, 0);
  692. }
  693. return this;
  694. },
  695. }
  696. // ___ _ ___ _
  697. // | _ \__ _ _ _ __| |___ _ __ | _ \ |__ _ _ _ ___ _ _
  698. // | / _` | ' \/ _` / _ \ ' \| _/ / _` | || / -_) '_|
  699. // |_|_\__,_|_||_\__,_\___/_|_|_|_| |_\__,_|\_, \___|_|
  700. // |__/
  701. function RandomPlayer(playlist){
  702. this.active = false;
  703. this.playlist = playlist;
  704. this.$btn = $('<a>').html('Shuffle').addClass('random-player-btn');
  705. this.init();
  706. };
  707. RandomPlayer.prototype = {
  708. init(){
  709. // this.shuffle();
  710. $('<div>')
  711. .addClass('block random-player')
  712. .append(this.$btn)
  713. // .insertAfter('#block-userlogin, #block-studiolinkblock');
  714. .prependTo('.region-footer-right');
  715. // events
  716. this.$btn.on('click', this.toggleActive.bind(this));
  717. // attach an event on AudioPlayer
  718. _audioPlayer
  719. .on('audio-ended', this.onAudioPlayerEnded.bind(this))
  720. .on('audio-play-next', this.onAudioPlayNext.bind(this))
  721. .on('stop-shuffle', this.stop.bind(this));
  722. },
  723. shuffle(){
  724. var tempPLaylist = [];
  725. for (var i = this.playlist.length-1; i >= 0 ; i--) {
  726. tempPLaylist.push(this.playlist[i]);
  727. }
  728. this.shuffledPlaylist = [];
  729. while(tempPLaylist.length > 0){
  730. var r = Math.floor(Math.random() * tempPLaylist.length);
  731. this.shuffledPlaylist.push(tempPLaylist.splice(r,1)[0]);
  732. }
  733. //console.log('RandomPlayer, this.shuffledPlaylist', this.shuffledPlaylist);
  734. },
  735. toggleActive(e){
  736. if (this.active) {
  737. this.stop();
  738. }else{
  739. this.start();
  740. }
  741. },
  742. start(){
  743. this.active = _audioPlayer.shuffle_is_active = true;
  744. this.$btn.addClass('is-active');
  745. this.shuffle();
  746. this.next();
  747. },
  748. stop(){
  749. this.active = _audioPlayer.shuffle_is_active = false;
  750. this.$btn.removeClass('is-active');
  751. // stop audio player
  752. // _audioPlayer.stop();
  753. },
  754. next(){
  755. if(this.active && this.shuffledPlaylist.length > 0)
  756. _audioPlayer.openDocument(this.shuffledPlaylist.splice(0,1)[0]);
  757. },
  758. onAudioPlayNext(){
  759. //console.log('RandomPlayer : onAudioPlayNext()');
  760. this.next();
  761. },
  762. onAudioPlayerEnded(){
  763. //console.log('RandomPlayer : onAudioPlayerEnded()');
  764. this.next();
  765. }
  766. };
  767. // ___ ___ _
  768. // / __|___ _ __ _ __ ___| _ \ |__ _ _ _ ___ _ _
  769. // | (__/ _ \ ' \| '_ \/ _ \ _/ / _` | || / -_) '_|
  770. // \___\___/_|_|_| .__/\___/_| |_\__,_|\_, \___|_|
  771. // |_| |__/
  772. function CompoPlayer(){
  773. this.active = false;
  774. this.playing = false;
  775. this.paused = false;
  776. this.playlist = [];
  777. this.current_index = 0;
  778. this.$composer = null;
  779. this.$compo = null;
  780. this.$controls = null;
  781. this.init();
  782. };
  783. CompoPlayer.prototype = {
  784. init(){
  785. // console.log('CompoPlayer init()');
  786. // attach an event on AudioPlayer
  787. _audioPlayer
  788. .on('audio-open-document', this.onAudioOpenDocument.bind(this))
  789. .on('audio-play', this.onAudioPlayerPlay.bind(this))
  790. .on('audio-pause', this.onAudioPlayerPause.bind(this))
  791. .on('audio-ended', this.onAudioPlayerEnded.bind(this));
  792. // .on('audio-play-next', this.onAudioPlayNext.bind(this));
  793. // this.newCompo();
  794. },
  795. newCompo(){
  796. //console.log('CompoPlayer newCompo()');
  797. // this.$compo = $('.composition_ui .composer .composition');
  798. this.initControls();
  799. },
  800. initControls(){
  801. //console.log('CompoPlayer initControls()');
  802. this.$composer = $('.composition_ui .composer');
  803. this.$compo = $('.composition_ui .composer .composition');
  804. this.$controls = $('.composition_ui .composer .compo-player-controls');
  805. if(!this.$controls.is('.ready') && this.$compo){
  806. this.$previous = $('<div>').addClass('previous')
  807. .on('click', this.prev.bind(this))
  808. .appendTo(this.$controls);
  809. this.$playpause = $('<div>').addClass('play-pause')
  810. .on('click', this.togglePlayPause.bind(this))
  811. .appendTo(this.$controls);
  812. this.$next = $('<div>').addClass('next')
  813. .on('click', this.next.bind(this))
  814. .appendTo(this.$controls);
  815. this.$controls.addClass('ready');
  816. this.refresh();
  817. this.active = true;
  818. // this.newCompo();
  819. }
  820. },
  821. refresh(){
  822. // console.log('CompoPlayer refresh(), this', this);
  823. this.stop();
  824. // load new playlist
  825. this.playlist = [];
  826. var that = this;
  827. $('.field--name-documents .field__item',this.$compo).each(function(i,el){
  828. var $link = $('a.audio-link',this);
  829. that.playlist.push({
  830. item:$(this),
  831. audio_url:$link.attr("audio_url"),
  832. nid:$link.attr("nid"),
  833. });
  834. });
  835. this.showHideControls();
  836. },
  837. togglePlayPause(){
  838. // console.log('CompoPlayer togglePlayPause');
  839. if (this.playing && !this.paused) {
  840. this.pause();
  841. }else{
  842. if(this.playing && this.paused){
  843. this.play();
  844. }else{
  845. this.start();
  846. }
  847. }
  848. },
  849. start(){
  850. //console.log('start');
  851. // console.log('CompoPlayer start()');
  852. this.playing = true;
  853. this.play();
  854. },
  855. play(){
  856. // console.log('play');
  857. if(this.paused){
  858. this.paused = false;
  859. _audioPlayer.play();
  860. }else{
  861. _audioPlayer.openDocument(this.playlist[this.current_index], this);
  862. }
  863. this.setActiveItem().showHideControls();
  864. },
  865. pause(){
  866. //console.log('pause');
  867. this.paused = true;
  868. this.showHideControls();
  869. _audioPlayer.stop();
  870. },
  871. next(){
  872. // console.log('CompoPlayer next()');
  873. if(this.playing){
  874. this.current_index += 1;
  875. if(this.current_index < this.playlist.length){
  876. this.play();
  877. }else{
  878. this.stop();
  879. }
  880. }
  881. },
  882. prev(){
  883. // console.log('CompoPlayer prev()');
  884. if(this.playing){
  885. this.current_index -= 1;
  886. if(this.current_index >= 0){
  887. this.play();
  888. }else{
  889. this.stop();
  890. }
  891. }
  892. },
  893. stop(){
  894. _audioPlayer.stop();
  895. this.reset();
  896. },
  897. reset(){
  898. this.playing = false;
  899. this.paused = false;
  900. this.resetIndex();
  901. },
  902. resetIndex(){
  903. this.current_index = 0;
  904. this.showHideControls().resetActiveItems();
  905. },
  906. setActiveItem(){
  907. this.resetActiveItems();
  908. if(this.playing && this.current_index >= 0){
  909. this.playlist[this.current_index].item.addClass('is-active');
  910. }
  911. // this call shoud not be here
  912. this.showHideControls();
  913. return this;
  914. },
  915. resetActiveItems(){
  916. for (var n = 0; n < this.playlist.length; n++) {
  917. // console.log('node',node);
  918. this.playlist[n].item.removeClass('is-active');
  919. }
  920. return this;
  921. },
  922. showHideControls(){
  923. // console.log('CompoPlayer showHideNextBtn(), playing:'+this.playing+', paused:'+this.paused);
  924. // global playing
  925. if(this.$controls){
  926. if(this.playing && !this.paused){
  927. this.$controls.addClass('is-playing');
  928. }else{
  929. this.$controls.removeClass('is-playing');
  930. }
  931. }
  932. // playpause
  933. if(this.$playpause){
  934. if(this.playlist.length > 0){
  935. this.$playpause.addClass('is-active');
  936. }else{
  937. this.$playpause.removeClass('is-active');
  938. }
  939. }
  940. // next
  941. if(this.$next){
  942. if(this.playing && this.playlist.length > 1 && this.current_index < this.playlist.length -1){
  943. this.$next.addClass('is-active');
  944. }else{
  945. this.$next.removeClass('is-active');
  946. }
  947. }
  948. // previous
  949. if(this.$previous){
  950. if(this.playing && this.playlist.length > 1 && this.current_index > 0){
  951. this.$previous.addClass('is-active');
  952. }else{
  953. this.$previous.removeClass('is-active');
  954. }
  955. }
  956. return this;
  957. },
  958. deactivate(){
  959. this.stop();
  960. this.active = false;
  961. },
  962. // _audioPlayer events
  963. onAudioOpenDocument(args){
  964. if(args.caller !== this){
  965. // console.log('CompoPlayer onAudioOpenDocument() called by other');
  966. this.reset();
  967. }
  968. // else{
  969. // // console.log('CompoPlayer onAudioOpenDocument() self calling');
  970. // }
  971. },
  972. onAudioPlayerPlay(){
  973. if(this.playing && this.paused){
  974. this.paused = false;
  975. this.showHideControls();
  976. }
  977. },
  978. onAudioPlayerPause(){
  979. if(this.playing && !this.paused){
  980. this.paused = true;
  981. this.showHideControls();
  982. }
  983. },
  984. onAudioPlayerEnded(){
  985. this.next();
  986. },
  987. // onAudioPlayNext(){
  988. // this.next();
  989. // }
  990. };
  991. // ___ _ ___
  992. // | __| _ ___ _ _| |_| _ \__ _ __ _ ___
  993. // | _| '_/ _ \ ' \ _| _/ _` / _` / -_)
  994. // |_||_| \___/_||_\__|_| \__,_\__, \___|
  995. // |___/
  996. function backToFrontPage(pop_state){
  997. closeAllModals();
  998. // assume we are going back to front page
  999. $('body').removeClass().addClass('path-frontpage');
  1000. $('a[data-drupal-link-system-path="<front>"]').addClass('is-active');
  1001. // close entrees
  1002. _$corpus_canvas.trigger({'type':'close-all-entree'});
  1003. if(!pop_state){
  1004. history.pushState({home:true}, null, window.location.origin);
  1005. }
  1006. }
  1007. function initHome(){
  1008. addCloseModalBtnToCols();
  1009. // console.log('theme : initHome');
  1010. // console.log('theme : initProductions');
  1011. var $grid = $('.grid',_$row).masonry({
  1012. itemSelector:'.col',
  1013. columnWidth:'.col-2',
  1014. horizontalOrder: true,
  1015. containerStyle: null,
  1016. // disable initial layout
  1017. // initLayout: false,
  1018. });
  1019. // bind event
  1020. // $grid.masonry( 'on', 'layoutComplete', function() {
  1021. // console.log('layout is complete');
  1022. // });
  1023. // layout Masonry after each image loads
  1024. $grid.imagesLoaded().progress( function() {
  1025. $grid.masonry('layout');
  1026. });
  1027. $grid.imagesLoaded(function(){
  1028. $grid.masonry('layout');
  1029. });
  1030. }
  1031. // ___ _ _ _
  1032. // | _ \_ _ ___ __| |_ _ __| |_(_)___ _ _ ___
  1033. // | _/ '_/ _ \/ _` | || / _| _| / _ \ ' \(_-<
  1034. // |_| |_| \___/\__,_|\_,_\__|\__|_\___/_||_/__/
  1035. function initProductions(){
  1036. // console.log('theme : initProductions');
  1037. var $grid = $('.grid',_$row).masonry({
  1038. itemSelector:'.col',
  1039. columnWidth:'.col-2',
  1040. horizontalOrder: true,
  1041. containerStyle: null,
  1042. // disable initial layout
  1043. // initLayout: false,
  1044. });
  1045. // bind event
  1046. // $grid.masonry( 'on', 'layoutComplete', function() {
  1047. // console.log('layout is complete');
  1048. // });
  1049. // layout Masonry after each image loads
  1050. $grid.imagesLoaded().progress( function() {
  1051. $grid.masonry('layout');
  1052. });
  1053. $grid.imagesLoaded(function(){
  1054. $grid.masonry('layout');
  1055. });
  1056. };
  1057. // __ __ _ _
  1058. // | \/ |___ __| |__ _| |___
  1059. // | |\/| / _ \/ _` / _` | (_-<
  1060. // |_| |_\___/\__,_\__,_|_/__/
  1061. function closeAllModals(){
  1062. //console.log('theme : closeAllModals');
  1063. // TODO: animate the remove
  1064. _$row.html('');
  1065. _$ajaxLinks.removeClass('is-active');
  1066. _$body.trigger({'type':'all-modal-closed'});
  1067. };
  1068. // _ _ _
  1069. // | || |___| |_ __ ___ _ _ ___
  1070. // | __ / -_) | '_ \/ -_) '_(_-<
  1071. // |_||_\___|_| .__/\___|_| /__/
  1072. // |_|
  1073. // https://plainjs.com/javascript/utilities/set-cookie-get-cookie-and-delete-cookie-5/
  1074. // function getCookie(name) {
  1075. // var v = document.cookie.match('(^|;) ?' + name + '=([^;]*)(;|$)');
  1076. // return v ? v[2] : null;
  1077. // }
  1078. // function setCookie(name, value, days) {
  1079. // var d = new Date;
  1080. // d.setTime(d.getTime() + 24*60*60*1000*days);
  1081. // document.cookie = name + "=" + value + ";path=/;expires=" + d.toGMTString();
  1082. // }
  1083. // function deleteCookie(name) { setCookie(name, '', -1); }
  1084. init();
  1085. } // end EdlpTheme()
  1086. $(document).ready(function($) {
  1087. var edlptheme = new EdlpTheme();
  1088. });
  1089. })(jQuery, Drupal, drupalSettings);