Playlist Editor (Advanced)

Learn how to use YouTube's API to search for videos and make a playlist. This makes authenticated requests with OAuth2. It uses jQuery++ for
drag/drop events. It shows using custom attributes and custom events. This guide takes
an hour to complete.

The async attribute allows other JS to execute while the api.js file is loading.
Once complete, this will call a googleScriptLoaded function.

Once api.js is loaded, it adds the gapi
object to the window. This is Google's JS API. It can be used to load other APIs that extend the gapi library.

The following can be used to load the OAuth2 GAPI libraries:

gapi.load('client:auth2', completeCallback);

Once this functionality is loaded, we can tell gapi to make requests on behalf of a registered
application. In this case, the following keys enable this client to make requests on behalf of the
"CanJS Playlist" application:

Get the user's name: googleAuth.currentUser.get().getBasicProfile().getGivenName()

ES5 Getter Syntax can
be used to define a DefineMap property that changes when another property changes. For example,
the following defines an signedOut property that is the opposite of the signedIn property:

Notice that drag is the 2nd argument to the event. You can listen to
drag events in can-stache and pass the drag argument to a function like:

on:draginit="startedDrag(%arguments[1])"

The drag.ghost() method copies the elements being dragged and drags that
instead. The .ghost() method returns the copied elements
wrapped with jQuery. Add the ghost className to style the ghost elements, like:

drag.ghost().addClass("ghost");

To add a method to a DefineMap, just add a function to one of the properties passed
to extend:

Drop videos

The problem

When the user drags a video over the playlist element, a placeholder of the
video will appear in the first position of the playlist.

If the video is dragged out of the playlist element, the placeholder will be removed.

If the video is dropped on the playlist element, it will be added to the playlist's
list of videos.

Prepare for inserting the placeholder or video in any position in the list.

What you need to know

The PlaylistVM should maintain a list of playlist videos (playlistVideos) and
the placeholder video (dropPlaceholderData) separately. It can combine these
two values into a single value (videosWithDropPlaceholder) of the videos to display to the
user. On a high-level, this might look like:

Drop videos in order

The problem

In this section, we will:

Allow a user to drop videos in order they prefer.

What you need to know

ViewModels are best left knowing very little about the DOM. This makes them more
easily unit-testable. To make this interaction, we need to know where the mouse
is in relation to the playlist's videos. This requires a lot of DOM interaction
and is best done outside the ViewModel.

Specifically, we'd like to translate the dropmove and dropon events
into other events that let people know where the dropmove and dropon events
are happening in relationship to the drop target's child elements.

Our goal is to:

Translate dropmove into sortableplaceholderat events
that dispatch events with the index where a placeholder should be inserted
and the dragData of what is being dragged.

Translate dropon into sortableinsertat events
that dispatch events with the index where the dragged item should be inserted
and the dragData of what is being dragged.

can.Control is useful for listening to events on an element in a memory-safe
way. Use extend to define a can.Control type, as follows: