| 
					
				 | 
			
			
				@@ -17,12 +17,15 @@ const PLAYER_WIDTH = 640 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 const PLAYER_HEIGHT = 360 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 let videos = null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+let videosPlayed = null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 let videoPlaying = null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 let state = null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 let isLeader = null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+let repeat = false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 let player = null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 let playerActive = false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 document.getElementById('player').append(mockPlayer('360', '640')); 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -96,11 +99,23 @@ function addVideo(code, id) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 function popVideo() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     console.log("pop", videos) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (videos.length <= 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (videosPlayed) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            videos = [] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            for (const { code, id } of videosPlayed) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                videos.push({code, id}); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                makeQueueLine(code, id); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            videosPlayed = [] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            state = PlayerState.LIST_END 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return undefined; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     const vid = videos.shift(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    if (vid !== undefined) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        queueElement.removeChild(queueElement.querySelector(".videoListCard")); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        state = PlayerState.LIST_END 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    queueElement.removeChild(queueElement.querySelector(".videoListCard")); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (videosPlayed) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        videosPlayed.push(vid); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     return vid; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -370,15 +385,25 @@ function onNextButton(event) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const repeatButton = document.getElementById('repeat-button'); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+function onRepeatButton(event) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    event.preventDefault(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    socket.send(makeMessage(MessageTypes.MEDIA_ACTION, { action: MediaAction.REPEAT, enable: !repeat })) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 function stateProcessor(ws, data) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    const { playing, state: newState, list } = data; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const { playing, state: newState, lists } = data; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const { next, previous } = lists 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     videos = [] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    videosPlayed = previous 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    repeat = !!previous 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     state = newState 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     videoPlaying = playing 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     const codes = [] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    for (const song of list) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    for (const song of next) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         const { code, id } = song 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         addVideo(code, id) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         if (!(codes.includes(code))) { 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -386,7 +411,16 @@ function stateProcessor(ws, data) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    socket.send(makeMessage(MessageTypes.SEARCH_ID, { id: codes })) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    for (const song of previous || []) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        const { code, id } = song; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (!(codes.includes(code))) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            codes.push(code) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (codes.length > 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        socket.send(makeMessage(MessageTypes.SEARCH_ID, { id: codes })) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     if (videoPlaying !== null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         if (state === PlayerState.PLAYING) { 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -398,6 +432,10 @@ function stateProcessor(ws, data) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     if (isLeader === null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         setLeader(false) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (repeat) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        repeatButton.classList.toggle('btn-outline-secondary'); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        repeatButton.classList.toggle('btn-secondary'); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     afterStateInit() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -405,7 +443,7 @@ function listOperationProcessor(ws, data) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     const { op, items } = data; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     if (op === ListOperationTypes.ADD) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         const noCodeInfo = [] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        for (const { code, id, snippet } of items){ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        for (const { code, id, snippet } of items) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             if (snippet !== undefined) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 codeInfoMap.set(code, snippet) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } else if (!codeInfoMap.has(code)) { 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -414,10 +452,10 @@ function listOperationProcessor(ws, data) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             addVideo(code, id); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         if (noCodeInfo.length > 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            socket.send(makeMessage(MessageTypes.SEARCH_ID, {id: noCodeInfo.join(',')})) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            socket.send(makeMessage(MessageTypes.SEARCH_ID, { id: noCodeInfo.join(',') })) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } else if (op === ListOperationTypes.DEL) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        for (const {id} of items) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        for (const { id } of items) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             delVideo(id); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } else if (op === ListOperationTypes.MOVE) { 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -428,7 +466,7 @@ function listOperationProcessor(ws, data) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 function mediaActionProcessor(ws, data) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    const { action, ended_id, current_id } = data; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const { action, ended_id, current_id, enable } = data; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     if (action === MediaAction.PLAY && state === PlayerState.PAUSED) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         state = PlayerState.PLAYING 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         player.playVideo(); 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -445,6 +483,13 @@ function mediaActionProcessor(ws, data) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 state = PlayerState.LIST_END; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } else if (action === MediaAction.REPEAT) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (enable !== repeat) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            repeat = enable; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            repeatButton.classList.toggle('btn-outline-secondary'); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            repeatButton.classList.toggle('btn-secondary'); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            videosPlayed = repeat ? (videoPlaying ? [videoPlaying] : []) : null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -494,7 +539,6 @@ function makeSearchResult(item) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     searchResult.setAttribute('data-youtubeID', code) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     function onClickHandler() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         socket.send(makeMessage(MessageTypes.LIST_OPERATION, { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             op: ListOperationTypes.ADD, 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -514,7 +558,11 @@ function makeSearchResult(item) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     const img = document.createElement('img') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     thumbnailImage.appendChild(img) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    const {url, width, height} = (thumbnails ? thumbnails["default"] : {url: "/img/no_thumbnail.png", width: 120, height: 90}) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const { url, width, height } = (thumbnails ? thumbnails["default"] : { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        url: "/img/no_thumbnail.png", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        width: 120, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        height: 90 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    }) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     img.setAttribute('src', url) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     img.setAttribute('width', width) 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -586,4 +634,5 @@ function afterStateInit() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     document.getElementById('play-button').addEventListener('click', onPlayButton) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     document.getElementById('pause-button').addEventListener('click', onPauseButton) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     document.getElementById('next-button').addEventListener('click', onNextButton) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    document.getElementById('repeat-button').addEventListener('click', onRepeatButton) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 |