├── demos ├── assets │ ├── images │ │ ├── user.png │ │ ├── channelid.png │ │ ├── default.png │ │ ├── playlists.png │ │ ├── responsive1.png │ │ └── responsive2.png │ ├── ie8 │ │ ├── getElementByClassName_polyfill.js │ │ ├── es5-sham.min.js │ │ └── es5-shim.min.js │ ├── demo.css │ ├── ytv.css │ ├── bootstrap.min.css │ └── ytv.js ├── fullscreen.html ├── chromeless.html ├── jquery.html ├── default.html ├── playlists.html ├── responsive.html ├── ie8.html ├── multiplayer.html └── testing.html ├── src ├── ie8 │ ├── getElementByClassName_polyfill.js │ ├── es5-sham.min.js │ └── es5-shim.min.js ├── ytv.css └── ytv.js ├── Todo.md ├── APIv2-3_Reference.md └── README.md /demos/assets/images/user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Giorgio003/Youtube-TV/HEAD/demos/assets/images/user.png -------------------------------------------------------------------------------- /demos/assets/images/channelid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Giorgio003/Youtube-TV/HEAD/demos/assets/images/channelid.png -------------------------------------------------------------------------------- /demos/assets/images/default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Giorgio003/Youtube-TV/HEAD/demos/assets/images/default.png -------------------------------------------------------------------------------- /demos/assets/images/playlists.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Giorgio003/Youtube-TV/HEAD/demos/assets/images/playlists.png -------------------------------------------------------------------------------- /demos/assets/images/responsive1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Giorgio003/Youtube-TV/HEAD/demos/assets/images/responsive1.png -------------------------------------------------------------------------------- /demos/assets/images/responsive2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Giorgio003/Youtube-TV/HEAD/demos/assets/images/responsive2.png -------------------------------------------------------------------------------- /src/ie8/getElementByClassName_polyfill.js: -------------------------------------------------------------------------------- 1 | document.getElementsByClassName = Element.prototype.getElementsByClassName = function(class_names) { 2 | class_names = (' ' + class_names) 3 | .replace(/[~!@$%^&*()_+\-=,./';:"?><[\]{}|`#]/g, '\\$&') 4 | .replace(/\s*(\s|$)/g, '$1') 5 | .replace(/\s/g, '.'); 6 | return this.querySelectorAll(class_names); 7 | }; -------------------------------------------------------------------------------- /demos/assets/ie8/getElementByClassName_polyfill.js: -------------------------------------------------------------------------------- 1 | document.getElementsByClassName = Element.prototype.getElementsByClassName = function(class_names) { 2 | class_names = (' ' + class_names) 3 | .replace(/[~!@$%^&*()_+\-=,./';:"?><[\]{}|`#]/g, '\\$&') 4 | .replace(/\s*(\s|$)/g, '$1') 5 | .replace(/\s/g, '.'); 6 | return this.querySelectorAll(class_names); 7 | }; -------------------------------------------------------------------------------- /demos/fullscreen.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | YouTube TV 5 | 6 | 7 | 13 | 14 | 15 |
16 | 17 | 18 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /demos/chromeless.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | YouTube TV 5 | 6 | 7 | 13 | 14 | 15 |
16 | 17 |
18 |

This demo autoplays videos, removes Youtube player chrome, and sets a custom accent color

19 |
20 | 21 | 22 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /demos/jquery.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | YouTube TV 5 | 6 | 7 | 13 | 14 | 15 | 16 |
17 | 18 |
19 |

This demo uses jQuery to active Youtube TV

20 |
21 | 22 | 23 | 24 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /demos/default.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | YouTube TV 5 | 6 | 7 | 13 | 14 | 15 |
16 | 17 |
18 |

This demo is done with a very minimal amount of options used.

19 |

The #frame element has dimensions of 400x800 before applying Youtube TV

20 |
21 | 22 | 23 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /demos/playlists.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | YouTube TV 5 | 6 | 7 | 13 | 14 | 15 | 16 |
17 | 18 |
19 |

This demo allows users to select playlists (if playlists exist)

20 |

← Click Here

21 |
22 | 23 | 24 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /Todo.md: -------------------------------------------------------------------------------- 1 | ## To-do list @Giorgio003 2 | 3 | - [x] Implement Responsive Design :iphone: 4 | - [x] Add additional colour scheme to match Youtube `light` player 5 | - [x] Create modular settings for responsiveness and themes 6 | - [x] Allow multiple players 7 | - [x] Merge remaining pending pull requests from Original 8 | - [x] Update documentation (ongoing) 9 | 10 | #### Youtube API v3 11 | - [x] Convert to Youtube API v3 !important 12 | - [ ] v3 pageToken - pages + full list (snowliondev) 13 | - [ ] Clean up code/documentation 14 | - [ ] Optimize API Quota usage (remove any unused variables left from v2) 15 | 16 | #### Additional 17 | - [x] Allow local testing (flag `http:` when testing locally) (later removed with APIv3/https) 18 | - [ ] FAQ/Fix common issues (Empty playlists, no uploads, etc.) 19 | - [ ] Load order, newest, random/shuffle. 20 | - [ ] Additional themes (or modular colour scheme) 21 | - [ ] Different Aspect ratios for responsive (4:3 for larger playist column) 22 | - [ ] Test IE 8-10 (Low Priority unless requested) 23 | - [x] Obtain More Coffee :coffee: 24 | - [ ] Any other suggestions welcome! :smiley: -------------------------------------------------------------------------------- /demos/assets/demo.css: -------------------------------------------------------------------------------- 1 | body{ 2 | background: #eee; 3 | font-family: arial, sans-serif; 4 | color: #222; 5 | font-size: 16px; 6 | } 7 | 8 | #frame, 9 | .description{ 10 | margin: 50px auto; 11 | width: 800px; 12 | height: 400px; 13 | } 14 | 15 | .playlists .special{ 16 | position: absolute; 17 | top: 50px; 18 | left: 50%; 19 | margin-left: 420px; 20 | } 21 | 22 | .playlists .ytv-list-header > a{ 23 | -webkit-animation: pulse 2s infinite; 24 | -moz-animation: pulse 2s infinite; 25 | -o-animation: pulse 2s infinite; 26 | animation: pulse 2s infinite; 27 | } 28 | .playlists .ytv-list-header.ytv-playlist-open > a{ 29 | -webkit-animation: none; 30 | -moz-animation: none; 31 | -o-animation: none; 32 | animation: none; 33 | } 34 | 35 | @-webkit-keyframes pulse { 36 | 0% { background: rgba(255,255,255,0); } 37 | 50% { background: rgba(255,255,255,0.1); } 38 | 100% { background: rgba(255,255,255,0); } 39 | } 40 | @-moz-keyframes pulse { 41 | 0% { background: rgba(255,255,255,0); } 42 | 50% { background: rgba(255,255,255,0.1); } 43 | 100% { background: rgba(255,255,255,0); } 44 | } 45 | @-o-keyframes pulse { 46 | 0% { background: rgba(255,255,255,0); } 47 | 50% { background: rgba(255,255,255,0.1); } 48 | 100% { background: rgba(255,255,255,0); } 49 | } 50 | @keyframes pulse { 51 | 0% { background: rgba(255,255,255,0); } 52 | 50% { background: rgba(255,255,255,0.1); } 53 | 100% { background: rgba(255,255,255,0); } 54 | -------------------------------------------------------------------------------- /demos/responsive.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | YouTube TV 5 | 6 | 7 | 8 | 15 | 16 | 17 |
18 |
19 |

This demo shows the player in a responsive template.

20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |

Responsive Demo

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum pretium varius gravida. Aliquam faucibus ex eget nunc semper, sit amet aliquet diam egestas. Nunc ultricies egestas augue. Aenean cursus, magna vel porttitor interdum, nisi nunc ultricies orci, quis rhoncus diam nisi sed massa. Sed tempor eros eget aliquet porttitor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum eget maximus sapien. Nam sed ligula lectus. Donec a nunc elit.

28 |
29 |
30 |
31 |
32 |

← Resize the window to see how the player adjusts. →

33 |
34 |
35 | 36 | 37 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /demos/ie8.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | YouTube TV 5 | 6 | 7 | 12 | 13 | 14 | 21 | 22 | 23 |
24 |
25 |

This demo shows the player in a test template for new features.

26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |

Testing Demo

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum pretium varius gravida. Aliquam faucibus ex eget nunc semper, sit amet aliquet diam egestas. Nunc ultricies egestas augue. Aenean cursus, magna vel porttitor interdum, nisi nunc ultricies orci, quis rhoncus diam nisi sed massa. Sed tempor eros eget aliquet porttitor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum eget maximus sapien. Nam sed ligula lectus. Donec a nunc elit.

34 |
35 |
36 |
37 |
38 | 39 | 40 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /demos/multiplayer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | YouTube TV 5 | 6 | 7 | 8 | 15 | 16 | 17 |
18 |
19 |

This demo shows the multiple players in a responsive template.

20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |

Multi-Responsive Demo

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum pretium varius gravida. Aliquam faucibus ex eget nunc semper, sit amet aliquet diam egestas. Nunc ultricies egestas augue. Aenean cursus, magna vel porttitor interdum, nisi nunc ultricies orci, quis rhoncus diam nisi sed massa. Sed tempor eros eget aliquet porttitor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum eget maximus sapien. Nam sed ligula lectus. Donec a nunc elit.

28 |
29 |
30 |
31 |
32 |

← Resize the window to see how the player adjusts. →

33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 | 42 | 43 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /demos/testing.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | YouTube TV 5 | 6 | 7 | 12 | 13 | 14 | 21 | 22 | 23 |
24 |
25 |

This demo shows the player in a test template for new features.

26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |

Testing Demo

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum pretium varius gravida. Aliquam faucibus ex eget nunc semper, sit amet aliquet diam egestas. Nunc ultricies egestas augue. Aenean cursus, magna vel porttitor interdum, nisi nunc ultricies orci, quis rhoncus diam nisi sed massa. Sed tempor eros eget aliquet porttitor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum eget maximus sapien. Nam sed ligula lectus. Donec a nunc elit.

34 |
35 |
36 |
37 |
38 | 39 | 40 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /src/ytv.css: -------------------------------------------------------------------------------- 1 | /* YouTube TV */ 2 | 3 | /* Base Canvas */ 4 | .ytv-canvas{ 5 | display: block; 6 | background: #282828; 7 | overflow: hidden; 8 | font-family: arial, sans-serif; 9 | } 10 | .ytv-canvas ::-webkit-scrollbar{ 11 | border-left: 1px solid #000; 12 | width: 10px; 13 | } 14 | .ytv-canvas ::-webkit-scrollbar-thumb{ 15 | background: rgba(255,255,255,0.1); 16 | } 17 | 18 | /* Video */ 19 | .ytv-video{ 20 | position: absolute; 21 | top: 0; 22 | right: 300px; 23 | bottom: 0; 24 | left: 0; 25 | height: 100%; 26 | } 27 | .ytv-video iframe{ 28 | width: 100%; 29 | height: 100%; 30 | border: none; 31 | outline: none; 32 | display: block; 33 | } 34 | 35 | /* List */ 36 | .ytv-list{ 37 | position: absolute; 38 | top: 0; 39 | right: 0; 40 | bottom: 0; 41 | height: 100%; 42 | width: 300px; 43 | } 44 | .ytv-list-inner{ 45 | overflow: auto; 46 | position: absolute; 47 | top: 52px; 48 | right: 0; 49 | bottom: 0; 50 | left: 0; 51 | -webkit-overflow-scrolling: touch; 52 | } 53 | .ytv-list ul{ 54 | margin: 0; 55 | padding: 0; 56 | list-style-type: none; 57 | } 58 | .ytv-list .ytv-active a{ 59 | border-left: 2px solid #fff; 60 | background: rgba(255,255,255,0.05);; 61 | } 62 | .ytv-list a{ 63 | display: block; 64 | text-decoration: none; 65 | font-size: 11px; 66 | color: #FEFEFE; 67 | padding: 10px; 68 | padding-left: 8px; 69 | border-top: 1px solid rgba(255,255,255,0.1); 70 | border-bottom: 1px solid rgba(0,0,0,0.5); 71 | border-left: 2px solid transparent; 72 | } 73 | .ytv-list a b{ 74 | max-height: 45px; 75 | overflow: hidden; 76 | display: block; 77 | text-overflow: ellipsis; 78 | } 79 | .ytv-list li:first-child a{ border-top: none; } 80 | .ytv-list li:last-child a{ border-bottom: none; } 81 | .ytv-list a:hover, 82 | .ytv-list-header .ytv-playlists a:hover{ background: rgba(255,255,255,0.05); } 83 | .ytv-list a:active, 84 | .ytv-list-header .ytv-playlists a:active{ background: rgba(0,0,0,0.05); } 85 | 86 | .ytv-list .ytv-content{ padding-left: 125px; } 87 | .ytv-list .ytv-thumb-stroke{ 88 | position: absolute; 89 | top: 1px; 90 | left: 1px; 91 | bottom: 1px; 92 | right: 1px; 93 | z-index: 2; 94 | outline: 1px solid rgba(255,255,255,0.1); 95 | } 96 | .ytv-list .ytv-thumb{ 97 | float: left; 98 | position: relative; 99 | outline: 1px solid rgba(0,0,0,0.5); 100 | } 101 | .ytv-list .ytv-thumb img{ 102 | width: 120px; 103 | display: block; 104 | } 105 | .ytv-list .ytv-thumb span{ 106 | position: absolute; 107 | bottom: 5px; 108 | right: 5px; 109 | color: #eee; 110 | background: rgba(0,0,0,0.7); 111 | font-size: 11px; 112 | font-weight: bold; 113 | padding: 0px 4px; 114 | 115 | -webkit-border-radius: 3px; 116 | -moz-border-radius: 3px; 117 | border-radius: 3px; 118 | } 119 | .ytv-views{ 120 | display: block; 121 | margin-top: 5px; 122 | font-size: 10px; 123 | font-weight: normal; 124 | opacity: 0.3; 125 | } 126 | 127 | .ytv-list-header{ 128 | height: 52px; 129 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); 130 | -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); 131 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); 132 | } 133 | .ytv-list-header a{ 134 | background: rgba(255,255,255,0.05); 135 | position: relative; 136 | z-index: 10; 137 | } 138 | .ytv-list-header img, 139 | .ytv-list .ytv-playlists .ytv-thumb img{ 140 | width: 30px; 141 | vertical-align: middle; 142 | } 143 | .ytv-list-header span{ 144 | padding-left: 10px; 145 | font-size: 12px; 146 | font-weight: bold; 147 | } 148 | 149 | /* Playlists */ 150 | .ytv-playlists{ 151 | z-index: 9; 152 | position: absolute; 153 | background: #282828; 154 | top:52px; 155 | left: 0; 156 | right: 0; 157 | bottom: 0; 158 | overflow: auto; 159 | display: none; 160 | } 161 | .ytv-playlists img, 162 | .ytv-list-header img{ 163 | float: left; 164 | } 165 | .ytv-playlists a span, 166 | .ytv-list-header a span{ 167 | white-space: nowrap; 168 | padding-left: 10px; 169 | display: block; 170 | overflow: hidden; 171 | text-overflow: ellipsis; 172 | } 173 | .ytv-list-header > a span{ 174 | line-height: 30px; 175 | } 176 | .ytv-list-header .ytv-playlists a{ 177 | background: none; 178 | } 179 | .ytv-playlist-open .ytv-playlists{ 180 | display: block; 181 | } 182 | 183 | /* Modifiers */ 184 | .ytv-relative{ 185 | position: relative; 186 | width: 100%; 187 | height: 100%; 188 | } 189 | .ytv-full{ 190 | position: fixed; 191 | top: 0; 192 | left: 0; 193 | width: 100% !important; 194 | height: 100% !important; 195 | margin: 0 !important; 196 | } 197 | .ytv-arrow { 198 | height: 10px; 199 | width: 0; 200 | position: relative; 201 | top: 10px; 202 | right: 5px; 203 | border: 10px solid transparent; 204 | float: right; 205 | border-top-color: rgba(0,0,0,0.4); 206 | display: none; 207 | } 208 | .ytv-has-playlists .ytv-arrow{ 209 | display: inline-block; 210 | } 211 | .ytv-playlist-open .ytv-arrow{ 212 | border-color: transparent; 213 | border-bottom-color: rgba(0,0,0,0.4); 214 | top: -10px; 215 | } 216 | 217 | .ytv-list-header a:after, 218 | .ytv-clear:after { 219 | content: "."; 220 | display: block; 221 | clear: both; 222 | visibility: hidden; 223 | line-height: 0; 224 | height: 0; 225 | } -------------------------------------------------------------------------------- /demos/assets/ytv.css: -------------------------------------------------------------------------------- 1 | /* YouTube TV */ 2 | 3 | /* Base Canvas */ 4 | .ytv-canvas{ 5 | display: block; 6 | background: #282828; 7 | overflow: hidden; 8 | font-family: arial, sans-serif; 9 | } 10 | .ytv-canvas ::-webkit-scrollbar{ 11 | border-left: 1px solid #000; 12 | width: 10px; 13 | } 14 | .ytv-canvas ::-webkit-scrollbar-thumb{ 15 | background: rgba(255,255,255,0.1); 16 | } 17 | 18 | /* Video */ 19 | .ytv-video{ 20 | position: absolute; 21 | top: 0; 22 | right: 300px; 23 | bottom: 0; 24 | left: 0; 25 | height: 100%; 26 | } 27 | .ytv-video iframe{ 28 | width: 100%; 29 | height: 100%; 30 | border: none; 31 | outline: none; 32 | display: block; 33 | } 34 | 35 | /* List */ 36 | .ytv-list{ 37 | position: absolute; 38 | top: 0; 39 | right: 0; 40 | bottom: 0; 41 | height: 100%; 42 | width: 300px; 43 | } 44 | .ytv-list-inner{ 45 | overflow: auto; 46 | position: absolute; 47 | top: 52px; 48 | right: 0; 49 | bottom: 0; 50 | left: 0; 51 | -webkit-overflow-scrolling: touch; 52 | } 53 | .ytv-list ul{ 54 | margin: 0; 55 | padding: 0; 56 | list-style-type: none; 57 | } 58 | .ytv-list .ytv-active a{ 59 | border-left: 2px solid #fff; 60 | background: rgba(255,255,255,0.05);; 61 | } 62 | .ytv-list a{ 63 | display: block; 64 | text-decoration: none; 65 | font-size: 11px; 66 | color: #FEFEFE; 67 | padding: 10px; 68 | padding-left: 8px; 69 | border-top: 1px solid rgba(255,255,255,0.1); 70 | border-bottom: 1px solid rgba(0,0,0,0.5); 71 | border-left: 2px solid transparent; 72 | } 73 | .ytv-list a b{ 74 | max-height: 45px; 75 | overflow: hidden; 76 | display: block; 77 | text-overflow: ellipsis; 78 | } 79 | .ytv-list li:first-child a{ border-top: none; } 80 | .ytv-list li:last-child a{ border-bottom: none; } 81 | .ytv-list a:hover, 82 | .ytv-list-header .ytv-playlists a:hover{ background: rgba(255,255,255,0.05); } 83 | .ytv-list a:active, 84 | .ytv-list-header .ytv-playlists a:active{ background: rgba(0,0,0,0.05); } 85 | 86 | .ytv-list .ytv-content{ padding-left: 125px; } 87 | .ytv-list .ytv-thumb-stroke{ 88 | position: absolute; 89 | top: 1px; 90 | left: 1px; 91 | bottom: 1px; 92 | right: 1px; 93 | z-index: 2; 94 | outline: 1px solid rgba(255,255,255,0.1); 95 | } 96 | .ytv-list .ytv-thumb{ 97 | float: left; 98 | position: relative; 99 | outline: 1px solid rgba(0,0,0,0.5); 100 | } 101 | .ytv-list .ytv-thumb img{ 102 | width: 120px; 103 | display: block; 104 | } 105 | .ytv-list .ytv-thumb span{ 106 | position: absolute; 107 | bottom: 5px; 108 | right: 5px; 109 | color: #eee; 110 | background: rgba(0,0,0,0.7); 111 | font-size: 11px; 112 | font-weight: bold; 113 | padding: 0px 4px; 114 | 115 | -webkit-border-radius: 3px; 116 | -moz-border-radius: 3px; 117 | border-radius: 3px; 118 | } 119 | .ytv-views{ 120 | display: block; 121 | margin-top: 5px; 122 | font-size: 10px; 123 | font-weight: normal; 124 | opacity: 0.3; 125 | } 126 | 127 | .ytv-list-header{ 128 | height: 52px; 129 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); 130 | -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); 131 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); 132 | } 133 | .ytv-list-header a{ 134 | background: rgba(255,255,255,0.05); 135 | position: relative; 136 | z-index: 10; 137 | } 138 | .ytv-list-header img, 139 | .ytv-list .ytv-playlists .ytv-thumb img{ 140 | width: 30px; 141 | vertical-align: middle; 142 | } 143 | .ytv-list-header span{ 144 | padding-left: 10px; 145 | font-size: 12px; 146 | font-weight: bold; 147 | } 148 | 149 | /* Playlists */ 150 | .ytv-playlists{ 151 | z-index: 9; 152 | position: absolute; 153 | background: #282828; 154 | top:52px; 155 | left: 0; 156 | right: 0; 157 | bottom: 0; 158 | overflow: auto; 159 | display: none; 160 | } 161 | .ytv-playlists img, 162 | .ytv-list-header img{ 163 | float: left; 164 | } 165 | .ytv-playlists a span, 166 | .ytv-list-header a span{ 167 | white-space: nowrap; 168 | padding-left: 10px; 169 | display: block; 170 | overflow: hidden; 171 | text-overflow: ellipsis; 172 | } 173 | .ytv-list-header > a span{ 174 | line-height: 30px; 175 | } 176 | .ytv-list-header .ytv-playlists a{ 177 | background: none; 178 | } 179 | .ytv-playlist-open .ytv-playlists{ 180 | display: block; 181 | } 182 | 183 | /* Modifiers */ 184 | .ytv-relative{ 185 | position: relative; 186 | width: 100%; 187 | height: 100%; 188 | } 189 | .ytv-full{ 190 | position: fixed; 191 | top: 0; 192 | left: 0; 193 | width: 100% !important; 194 | height: 100% !important; 195 | margin: 0 !important; 196 | } 197 | .ytv-arrow { 198 | height: 10px; 199 | width: 0; 200 | position: relative; 201 | top: 10px; 202 | right: 5px; 203 | border: 10px solid transparent; 204 | float: right; 205 | border-top-color: rgba(0,0,0,0.4); 206 | display: none; 207 | } 208 | .ytv-has-playlists .ytv-arrow{ 209 | display: inline-block; 210 | } 211 | .ytv-playlist-open .ytv-arrow{ 212 | border-color: transparent; 213 | border-bottom-color: rgba(0,0,0,0.4); 214 | top: -10px; 215 | } 216 | 217 | .ytv-list-header a:after, 218 | .ytv-clear:after { 219 | content: "."; 220 | display: block; 221 | clear: both; 222 | visibility: hidden; 223 | line-height: 0; 224 | height: 0; 225 | } -------------------------------------------------------------------------------- /src/ie8/es5-sham.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * https://github.com/es-shims/es5-shim 3 | * @license es5-shim Copyright 2009-2015 by contributors, MIT License 4 | * see https://github.com/es-shims/es5-shim/blob/v4.1.7/LICENSE 5 | */ 6 | (function(e,t){"use strict";if(typeof define==="function"&&define.amd){define(t)}else if(typeof exports==="object"){module.exports=t()}else{e.returnExports=t()}})(this,function(){var e=Function.prototype.call;var t=Object.prototype;var r=e.bind(t.hasOwnProperty);var n;var o;var c;var i;var f=r(t,"__defineGetter__");if(f){n=e.bind(t.__defineGetter__);o=e.bind(t.__defineSetter__);c=e.bind(t.__lookupGetter__);i=e.bind(t.__lookupSetter__)}if(!Object.getPrototypeOf){Object.getPrototypeOf=function x(e){var r=e.__proto__;if(r||r===null){return r}else if(e.constructor){return e.constructor.prototype}else{return t}}}var l=function z(e){try{e.sentinel=0;return Object.getOwnPropertyDescriptor(e,"sentinel").value===0}catch(t){return false}};if(Object.defineProperty){var a=l({});var u=typeof document==="undefined"||l(document.createElement("div"));if(!u||!a){var p=Object.getOwnPropertyDescriptor}}if(!Object.getOwnPropertyDescriptor||p){var b="Object.getOwnPropertyDescriptor called on a non-object: ";Object.getOwnPropertyDescriptor=function S(e,n){if(typeof e!=="object"&&typeof e!=="function"||e===null){throw new TypeError(b+e)}if(p){try{return p.call(Object,e,n)}catch(o){}}var l;if(!r(e,n)){return l}l={enumerable:true,configurable:true};if(f){var a=e.__proto__;var u=e!==t;if(u){e.__proto__=t}var s=c(e,n);var O=i(e,n);if(u){e.__proto__=a}if(s||O){if(s){l.get=s}if(O){l.set=O}return l}}l.value=e[n];l.writable=true;return l}}if(!Object.getOwnPropertyNames){Object.getOwnPropertyNames=function D(e){return Object.keys(e)}}if(!Object.create){var s;var O=!({__proto__:null}instanceof Object);var j=function k(){if(!document.domain){return false}try{return!!new ActiveXObject("htmlfile")}catch(e){return false}};var d=function F(){var e;var t;t=new ActiveXObject("htmlfile");t.write("");t.close();e=t.parentWindow.Object.prototype;t=null;return e};var y=function G(){var e=document.createElement("iframe");var t=document.body||document.documentElement;var r;e.style.display="none";t.appendChild(e);e.src="javascript:";r=e.contentWindow.Object.prototype;t.removeChild(e);e=null;return r};if(O||typeof document==="undefined"){s=function(){return{__proto__:null}}}else{s=function(){var e=j()?d():y();delete e.constructor;delete e.hasOwnProperty;delete e.propertyIsEnumerable;delete e.isPrototypeOf;delete e.toLocaleString;delete e.toString;delete e.valueOf;e.__proto__=null;var t=function r(){};t.prototype=e;s=function(){return new t};return new t}}Object.create=function A(e,t){var r;var n=function o(){};if(e===null){r=s()}else{if(typeof e!=="object"&&typeof e!=="function"){throw new TypeError("Object prototype may only be an Object or null")}n.prototype=e;r=new n;r.__proto__=e}if(t!==void 0){Object.defineProperties(r,t)}return r}}var _=function C(e){try{Object.defineProperty(e,"sentinel",{});return"sentinel"in e}catch(t){return false}};if(Object.defineProperty){var v=_({});var w=typeof document==="undefined"||_(document.createElement("div"));if(!v||!w){var h=Object.defineProperty,m=Object.defineProperties}}if(!Object.defineProperty||h){var P="Property description must be an object: ";var E="Object.defineProperty called on non-object: ";var g="getters & setters can not be defined on this javascript engine";Object.defineProperty=function N(e,r,l){if(typeof e!=="object"&&typeof e!=="function"||e===null){throw new TypeError(E+e)}if(typeof l!=="object"&&typeof l!=="function"||l===null){throw new TypeError(P+l)}if(h){try{return h.call(Object,e,r,l)}catch(a){}}if("value"in l){if(f&&(c(e,r)||i(e,r))){var u=e.__proto__;e.__proto__=t;delete e[r];e[r]=l.value;e.__proto__=u}else{e[r]=l.value}}else{if(!f){throw new TypeError(g)}if("get"in l){n(e,r,l.get)}if("set"in l){o(e,r,l.set)}}return e}}if(!Object.defineProperties||m){Object.defineProperties=function W(e,t){if(m){try{return m.call(Object,e,t)}catch(r){}}Object.keys(t).forEach(function(r){if(r!=="__proto__"){Object.defineProperty(e,r,t[r])}});return e}}if(!Object.seal){Object.seal=function X(e){if(Object(e)!==e){throw new TypeError("Object.seal can only be called on Objects.")}return e}}if(!Object.freeze){Object.freeze=function I(e){if(Object(e)!==e){throw new TypeError("Object.freeze can only be called on Objects.")}return e}}try{Object.freeze(function(){})}catch(T){Object.freeze=function(e){return function t(r){if(typeof r==="function"){return r}else{return e(r)}}}(Object.freeze)}if(!Object.preventExtensions){Object.preventExtensions=function L(e){if(Object(e)!==e){throw new TypeError("Object.preventExtensions can only be called on Objects.")}return e}}if(!Object.isSealed){Object.isSealed=function q(e){if(Object(e)!==e){throw new TypeError("Object.isSealed can only be called on Objects.")}return false}}if(!Object.isFrozen){Object.isFrozen=function B(e){if(Object(e)!==e){throw new TypeError("Object.isFrozen can only be called on Objects.")}return false}}if(!Object.isExtensible){Object.isExtensible=function H(e){if(Object(e)!==e){throw new TypeError("Object.isExtensible can only be called on Objects.")}var t="";while(r(e,t)){t+="?"}e[t]=true;var n=r(e,t);delete e[t];return n}}}); 7 | //# sourceMappingURL=es5-sham.map 8 | -------------------------------------------------------------------------------- /demos/assets/ie8/es5-sham.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * https://github.com/es-shims/es5-shim 3 | * @license es5-shim Copyright 2009-2015 by contributors, MIT License 4 | * see https://github.com/es-shims/es5-shim/blob/v4.1.7/LICENSE 5 | */ 6 | (function(e,t){"use strict";if(typeof define==="function"&&define.amd){define(t)}else if(typeof exports==="object"){module.exports=t()}else{e.returnExports=t()}})(this,function(){var e=Function.prototype.call;var t=Object.prototype;var r=e.bind(t.hasOwnProperty);var n;var o;var c;var i;var f=r(t,"__defineGetter__");if(f){n=e.bind(t.__defineGetter__);o=e.bind(t.__defineSetter__);c=e.bind(t.__lookupGetter__);i=e.bind(t.__lookupSetter__)}if(!Object.getPrototypeOf){Object.getPrototypeOf=function x(e){var r=e.__proto__;if(r||r===null){return r}else if(e.constructor){return e.constructor.prototype}else{return t}}}var l=function z(e){try{e.sentinel=0;return Object.getOwnPropertyDescriptor(e,"sentinel").value===0}catch(t){return false}};if(Object.defineProperty){var a=l({});var u=typeof document==="undefined"||l(document.createElement("div"));if(!u||!a){var p=Object.getOwnPropertyDescriptor}}if(!Object.getOwnPropertyDescriptor||p){var b="Object.getOwnPropertyDescriptor called on a non-object: ";Object.getOwnPropertyDescriptor=function S(e,n){if(typeof e!=="object"&&typeof e!=="function"||e===null){throw new TypeError(b+e)}if(p){try{return p.call(Object,e,n)}catch(o){}}var l;if(!r(e,n)){return l}l={enumerable:true,configurable:true};if(f){var a=e.__proto__;var u=e!==t;if(u){e.__proto__=t}var s=c(e,n);var O=i(e,n);if(u){e.__proto__=a}if(s||O){if(s){l.get=s}if(O){l.set=O}return l}}l.value=e[n];l.writable=true;return l}}if(!Object.getOwnPropertyNames){Object.getOwnPropertyNames=function D(e){return Object.keys(e)}}if(!Object.create){var s;var O=!({__proto__:null}instanceof Object);var j=function k(){if(!document.domain){return false}try{return!!new ActiveXObject("htmlfile")}catch(e){return false}};var d=function F(){var e;var t;t=new ActiveXObject("htmlfile");t.write("");t.close();e=t.parentWindow.Object.prototype;t=null;return e};var y=function G(){var e=document.createElement("iframe");var t=document.body||document.documentElement;var r;e.style.display="none";t.appendChild(e);e.src="javascript:";r=e.contentWindow.Object.prototype;t.removeChild(e);e=null;return r};if(O||typeof document==="undefined"){s=function(){return{__proto__:null}}}else{s=function(){var e=j()?d():y();delete e.constructor;delete e.hasOwnProperty;delete e.propertyIsEnumerable;delete e.isPrototypeOf;delete e.toLocaleString;delete e.toString;delete e.valueOf;e.__proto__=null;var t=function r(){};t.prototype=e;s=function(){return new t};return new t}}Object.create=function A(e,t){var r;var n=function o(){};if(e===null){r=s()}else{if(typeof e!=="object"&&typeof e!=="function"){throw new TypeError("Object prototype may only be an Object or null")}n.prototype=e;r=new n;r.__proto__=e}if(t!==void 0){Object.defineProperties(r,t)}return r}}var _=function C(e){try{Object.defineProperty(e,"sentinel",{});return"sentinel"in e}catch(t){return false}};if(Object.defineProperty){var v=_({});var w=typeof document==="undefined"||_(document.createElement("div"));if(!v||!w){var h=Object.defineProperty,m=Object.defineProperties}}if(!Object.defineProperty||h){var P="Property description must be an object: ";var E="Object.defineProperty called on non-object: ";var g="getters & setters can not be defined on this javascript engine";Object.defineProperty=function N(e,r,l){if(typeof e!=="object"&&typeof e!=="function"||e===null){throw new TypeError(E+e)}if(typeof l!=="object"&&typeof l!=="function"||l===null){throw new TypeError(P+l)}if(h){try{return h.call(Object,e,r,l)}catch(a){}}if("value"in l){if(f&&(c(e,r)||i(e,r))){var u=e.__proto__;e.__proto__=t;delete e[r];e[r]=l.value;e.__proto__=u}else{e[r]=l.value}}else{if(!f){throw new TypeError(g)}if("get"in l){n(e,r,l.get)}if("set"in l){o(e,r,l.set)}}return e}}if(!Object.defineProperties||m){Object.defineProperties=function W(e,t){if(m){try{return m.call(Object,e,t)}catch(r){}}Object.keys(t).forEach(function(r){if(r!=="__proto__"){Object.defineProperty(e,r,t[r])}});return e}}if(!Object.seal){Object.seal=function X(e){if(Object(e)!==e){throw new TypeError("Object.seal can only be called on Objects.")}return e}}if(!Object.freeze){Object.freeze=function I(e){if(Object(e)!==e){throw new TypeError("Object.freeze can only be called on Objects.")}return e}}try{Object.freeze(function(){})}catch(T){Object.freeze=function(e){return function t(r){if(typeof r==="function"){return r}else{return e(r)}}}(Object.freeze)}if(!Object.preventExtensions){Object.preventExtensions=function L(e){if(Object(e)!==e){throw new TypeError("Object.preventExtensions can only be called on Objects.")}return e}}if(!Object.isSealed){Object.isSealed=function q(e){if(Object(e)!==e){throw new TypeError("Object.isSealed can only be called on Objects.")}return false}}if(!Object.isFrozen){Object.isFrozen=function B(e){if(Object(e)!==e){throw new TypeError("Object.isFrozen can only be called on Objects.")}return false}}if(!Object.isExtensible){Object.isExtensible=function H(e){if(Object(e)!==e){throw new TypeError("Object.isExtensible can only be called on Objects.")}var t="";while(r(e,t)){t+="?"}e[t]=true;var n=r(e,t);delete e[t];return n}}}); 7 | //# sourceMappingURL=es5-sham.map 8 | -------------------------------------------------------------------------------- /APIv2-3_Reference.md: -------------------------------------------------------------------------------- 1 | #APIv2 to APIv3 Reference Notes 2 | 3 | Notes to reference for the conversion for a better understanding on what has changed for myself and others. 4 | 5 | ## Youtube-TV Elements Chart 6 | 7 | Currently used elements in `ytv.js` and their new APIv3 counterparts. 8 | 9 | #### User (channel) Playlists 10 | 11 | playlists = | res.feed.entry 12 | ----------- | -------------- 13 | | **res.feed.items** 14 | 15 | `playlists[i]` 16 | 17 | Element | Old Value | New Value 18 | ------- | --------- | --------- 19 | title | .title.$t | .snippet.title 20 | plid | .yt$playlistId.$t | .id 21 | thumb | .media$group.media$thumbnail[1].url | .snippet.thumbnails.medium.url 22 | 23 | #### User (channel) Info 24 | 25 | user = | userInfo.entry 26 | ------ | -------------- 27 | | **userInfo.items[0]** 28 | 29 | Element | Old Value | New Value 30 | ------- | --------- | --------- 31 | title | .title.$t | .snippet.title 32 | url | .yt$username.$t | .id 33 | thumb | .media$thumbnail.url | .snippet.thumbnails.default.url 34 | summary | .summary.$t | .snippet.description 35 | subscribers | .yt$statistics.subscriberCount | .statistics.subscriberCount 36 | views | .yt$statistics.totalUploadViews | .statistics.viewCount 37 | **`NEW`** | - | - 38 | uploads | n/a | .contentDetails.relatedPlaylists.uploads 39 | 40 | To support newer accounts by using channel ID instead of user ID. 41 | **Old:** `url: local+'//youtube.com/user/'+userInfo.entry.yt$username.$t` 42 | **New:** `url: 'https://youtube.com/channel/'+userInfo.id` 43 | 44 | #### `NEW` Playlist Videos 45 | 46 | playlistVideos = | n/a 47 | ---------------- | --- 48 | | **res.feed.items** 49 | 50 | `plistlistVideos[i]` 51 | 52 | Element | Old Value | New Value 53 | ------- | --------- | --------- 54 | slug | n/a | .contentDetails.videoId 55 | 56 | #### Video Info 57 | 58 | videos = | data.feed.entry 59 | -------- | --------------- 60 | | **data.feed.items** 61 | 62 | `videos[i]` 63 | 64 | Element | Old Value | New Value 65 | ------- | --------- | --------- 66 | title | .title.$t | .snippet.title 67 | *slug | .media$group.yt$videoid.$t | .id 68 | link | .link[0].href | n/a *use slug 69 | published | .published.$t | .snippet.publishedAt 70 | rating | .yt$rating | n/a *see statistics 71 | stats | .yt$statistics | .statistics 72 | duration | ( .media$group.yt$duration.seconds) | .contentDetails.duration 73 | thumb | .media$group.media$thumbnail[1].url | .snippet.thumbnails.medium.url 74 | **`NEW`** | - | - 75 | embed | n/a | .status.embeddable 76 | 77 | ## Updated URLs 78 | #### base 79 | 80 | base = | `local+'//gdata.youtube.com/'` 81 | ------ | ---------------------------- 82 | | **`'https://www.googleapis.com/youtube/v3/'`** 83 | 84 | * *https required for APIv3* 85 | 86 | #### userInfo 87 | ``` 88 | Before: 89 | utils.endpoints.base+'feeds/api/users/'+settings.user+'?v=2&alt=json'; 90 | After: 91 | utils.endpoints.base+'channels?'+settings.cid+'&key='+apiKey+'&part=snippet,contentDetails,statistics'; 92 | ``` 93 | **Required in Build** 94 | ```javascript 95 | if (settings.channelId){ 96 | settings.cid = 'id='+settings.channelId; 97 | } else if(settings.user){ 98 | settings.cid = 'forUsername='+settings.user; 99 | } 100 | ``` 101 | 102 | #### userVids 103 | ``` 104 | Before: 105 | utils.endpoints.base+'feeds/api/users/'+settings.user+'/uploads/?v=2&alt=json&format=5&max-results=50'; 106 | After: n/a pulled playlistId pulled from userInfo 107 | ``` 108 | **Replaced with:** 109 | ```javascript 110 | userUploads: function(userInfo){ 111 | if (userInfo && userInfo.items){ 112 | settings.playlist = userInfo.items[0].contentDetails.relatedPlaylists.uploads; 113 | utils.ajax.get( utils.endpoints.playlistVids(), prepare.compileVideos ); 114 | } 115 | } 116 | ``` 117 | 118 | #### userPlaylists 119 | ``` 120 | Before: 121 | utils.endpoints.base+'feeds/api/users/'+settings.user+'/playlists/?v=2&alt=json&format=5&max-results=50'; 122 | After: 123 | utils.endpoints.base+'playlists?channelId='+settings.channelId+'&key='+apiKey+'&maxResults=50&part=snippet'; 124 | ``` 125 | 126 | #### playlistVids 127 | ``` 128 | Before: 129 | utils.endpoints.base+'feeds/api/playlists/'+(settings.playlist)+'?v=2&alt=json&format=5&max-results=50'; 130 | After: 131 | utils.endpoints.base+'playlistItems?playlistId='+settings.playlist+'&key='+apiKey+'&maxResults=50&part=contentDetails'; 132 | ``` 133 | 134 | #### `NEW` playlistInfo 135 | `utils.endpoints.base+'playlists?id='+settings.playlist+'&key='+apiKey+'&maxResults=50&part=snippet';` 136 | ```javascript 137 | selectedPlaylist: function(playlistInfo){ 138 | if (playlistInfo && playlistInfo.items) { 139 | settings.currentPlaylist = playlistInfo.items[0].snippet.title; 140 | utils.ajax.get( utils.endpoints.playlistVids(), prepare.compileVideos ); 141 | } 142 | } 143 | ``` 144 | 145 | #### `NEW` videoInfo 146 | `utils.endpoints.base+'videos?id='+settings.videoString+'&key='+apiKey+'&maxResults=50&part=snippet,contentDetails,status,statistics';` 147 | ```javascript 148 | compileVideos: function(res){ 149 | if (res && res.items){ 150 | var playlists = res.items, 151 | i; 152 | settings.videoString = ''; 153 | for(i=0; i 9 ? '' + amount : '0' + amount); break; 174 | case 'M': m = (amount > 9 ? '' + amount : '0' + amount); break; 175 | case 'S': s = (amount > 9 ? '' + amount : '0' + amount); break; 176 | default: // ??? profit 177 | } 178 | }); 179 | if (h){ time += h+':';} 180 | if (m){ time += m+':';} else { time += '00:';} 181 | if (s){ time += s;} else { time += '00';} 182 | 183 | return time; 184 | } 185 | ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # YoutubeTV 2 | **NOW with API Version 3 Support!!** 3 | A small, slick, library independent YouTube User/Playlist player 4 | 5 | 6 | 7 | ## Features 8 | * Library Independent 9 | * Customizable via CSS 10 | * Autoplay 11 | * Remove YouTube Chrome 12 | * Supports Playlists and Users 13 | * jQuery Support 14 | * Module Support 15 | * Responsive Support 16 | * Alternative color scheme 17 | 18 | ## Demos 19 | * [Default Player Options](http://giorgio003.github.io/Youtube-TV/demos/default.html) 20 | * [Chromeless Player](http://giorgio003.github.io/Youtube-TV/demos/chromeless.html) 21 | * [Playlist Support](http://giorgio003.github.io/Youtube-TV/demos/playlists.html) 22 | * [Full Screen Player](http://giorgio003.github.io/Youtube-TV/demos/fullscreen.html) (Good for a .tv website?) 23 | * [jQuery Support](http://giorgio003.github.io/Youtube-TV/demos/jquery.html) 24 | * [Responsive Support](http://giorgio003.github.io/Youtube-TV/demos/responsive.html) 25 | * [Multiple Players](http://giorgio003.github.io/Youtube-TV/demos/multiplayer.html) 26 | 27 | ## Obtaining Youtube API Key 28 | 29 | As part of the update to [Youtube's API version 3](https://developers.google.com/youtube/v3/), it is required to obtain an API Key from [Google's Developer Console](https://console.developers.google.com/). 30 | 31 | To obtain your API Key you can follow this guide using steps 1 to 3. 32 | [https://developers.google.com/youtube/v3/getting-started](https://developers.google.com/youtube/v3/getting-started) 33 | 34 | Then: 35 | 4. under APIs & auth > Credentials > Create new Key > (Select) Browser Key 36 | 5. (Recommended) Set any referrers to your domain to prevent unauthorized use of your key. 37 | 6. (Optional) Additionally disabling any other unused APIs that are enabled. 38 | 39 | ## Installation 40 | 41 | After obtaining your API Key, fill it in `ytv.js` near the top via this line: 42 | `var apiKey = 'YOUR_API_KEY_HERE';` 43 | 44 | Include both the `ytv.css` and `ytv.js` in your HTML file and you are good to go. 45 | 46 | ```html 47 | 48 | 49 | ``` 50 | 51 | ## Usage 52 | 53 | After your page has loaded, you may call the script like so: 54 | ```html 55 |
56 | ``` 57 | ```javascript 58 | document.addEventListener("DOMContentLoaded", function(event) { 59 | var controller = new YTV('YourPlayerID', { 60 | user: 'YoutubeUsername' 61 | }); 62 | }); 63 | ``` 64 | **Technical Note:** Above `DOMContentLoaded` is preferred for speed on modern browsers (additionally works down to IE9). However the demos are mostly using `window.onload` which is a slower option but compatible with older browsers (IE8 or older). Good examples of this difference can be found [here (microsoft.com)](http://ie.microsoft.com/Testdrive/HTML5/DOMContentLoaded/Default.html) and [here (jspref.com)](http://jsperf.com/onload-vs-domcontentloaded/3) 65 | 66 | **`Note:`** If you are using a newer YouTube channel you may have a **Channel ID** which may look similar to this: `UCXXXXXXXXXXXXXXXXXXXXXX` 67 | If so, use `channelId: 'UCXXXXXXXXXXXXXXXXXXXXXX'` to replace user! 68 | 69 | #### User vs Channel ID 70 | By visiting the intended channel's Youtube home page. The URL should look like one of the following. 71 | ##### User ID `user: ''` 72 | 73 | 74 | ##### Channel ID `channelId: ''` 75 | 76 | 77 | #### Additional Options 78 | 79 | Additional options you may wish to add to get started. 80 | 81 | In addition to `user` and `channelId` you can also use `playlist` ID's which can also replace `user` and `channelId` or work along side them. 82 | `playlist: 'PLAYLIST_ID_HERE'` 83 | To chain multiple playlists together, separate them by comma's. Example: 84 | `playlist: 'PLAYLIST_ID1,PLAYLIST_ID2,PLAYLIST_ID3'` 85 | 86 | Add this to enable the responsive Youtube-TV player allowing the player to adjust from desktop resolution all the way to mobile devices. 87 | `responsive: true` 88 | 89 | Accent colour will appear beside the active video in the list. This will accept any CSS value from `#FFF` to `orange`. 90 | `accent: '#008D54'` 91 | 92 | If you prefer the `light` theme over the dark, add these 2 options (or mix 'n match if desired). 93 | `playerTheme: 'light'` 94 | `listTheme: 'light'` 95 | 96 | **Don't forget comma's between each option!** 97 | 98 | `YTV` accepts two parameters. The first is a string of the element ID you want to use as the player, OR it may the element itself. The second parameter is an object of options defined below: 99 | 100 | ## Settings, Defaults and Details 101 | ```javascript 102 | settings = { 103 | element: null, 104 | user: null, 105 | channelId: null, 106 | playlist: '', 107 | fullscreen: false, 108 | accent: '#fff', 109 | controls: true, 110 | annotations: false, 111 | autoplay: false, 112 | chainVideos: true, 113 | browsePlaylists: false, 114 | playerTheme: 'dark', 115 | listTheme: 'dark', 116 | responsive: false, 117 | playId:'', 118 | sortList: false, 119 | reverseList: false, 120 | shuffleList: false, 121 | wmode: 'opaque', 122 | events: { 123 | videoReady: function(){}, 124 | stateChange: function(){} 125 | } 126 | } 127 | ``` 128 | 129 | * `element`: The element or element ID to apply the YouTube TV Player to 130 | * `user`: (String) The Username of the YouTube user you want to display videos from 131 | * `channelId`: (String) The Channel ID of the YouTube channel you want to display videos from (for newer accounts) 132 | * `playlist`: (String) The Playlist ID(s) you would like to load separated by comma's (Overrides `user`) 133 | * `browsePlaylists`: (Boolean) If `true` and the specified `user` has YouTube playlists, they will be accessible in the player by clicking the users Username 134 | * `fullscreen`: (Boolean) If `true`, the player will take up all the available space on the users browser screen 135 | * `accent`: (String) A CSS color string to apply to the accents of the player 136 | * `controls`: (Boolean) If `false`, the normal YouTube controls will be hidden. 137 | * `annotations`: (Boolean) If `false`, the annotations from the YouTube video will be hidden 138 | * `autoplay`: (Boolean) If `true`, the first video in the list will automatically play once the player has loaded 139 | * `chainVideos`: (Boolean) If `true`, the next video in que will automatically play after the current video has completed 140 | * `playerTheme`: (String) Sets the youtube player theme. Default is `dark` with an alterative `light` color scheme. 141 | * `listTheme`: (String) Sets the playlist theme. Default is `dark` with a `light` theme to match the alternative player theme. 142 | * `responsive`: (Boolean) If `true`, it enables a responsive design to support various resolutions including mobile devices. Default currently `false`. 143 | * `playId`: (String) On load this option will allow you to select the first video to play/display. Enter the id of the video you want to display. (ie. `.../watch?v=VIDEOID` to `playId: 'VIDEOID'`). 144 | * `sortList`: (Boolean) If `true`, this option will sort the video list by date uploaded. 145 | * `reverseList`: (Boolean) If `true`, this option will reverse order of the videos. (Can be combined with other sorting option to reverse after sorting). 146 | * `shuffleList`: (Boolean) If `true`, this option will shuffle the video list. (Useful for music playlists). 147 | * `wmode`: (String) Sets the Window Mode property for transparency, layering, and positioning in the browser. Values can be: `window` - movie plays in its own rectangular window on a web page. `opaque` - the movie hides everything on the page behind it. `transparent` - the background of the HTML page shows through all transparent portions of the movie, this may slow animation performance. 148 | * `events`: The respective events will fire when key actions in the player have been called 149 | 150 | 151 | ## Responsive Design 152 | Youtube-TV can now adapt to various changes in resolution based on it's parent canvas/container. Currently set to `max-width:992px` to match Bootstrap's responsive transitions, but can be modified to match any other responsive boilerplate. 153 | 154 | #### Playlist beside 155 | *(image resized on github don't be fooled >_<)* 156 | 157 | #### Playlist on bottom 158 | *Better for mobile devices* 159 | 160 | 161 | ## Public Methods 162 | 163 | ### `destroy` 164 | Destroys the YouTube TV Player and all associated elements/events 165 | 166 | ```javascript 167 | controller.destroy(); 168 | ``` 169 | 170 | ### `fullscreen.state()` 171 | Returns `true` if in Fullscreen Mode 172 | 173 | ```javascript 174 | controller.fullscreen.state(); 175 | ``` 176 | 177 | ### `fullscreen.enter()` 178 | Enters into Fullscreen Mode 179 | 180 | ```javascript 181 | controller.fullscreen.enter(); 182 | ``` 183 | 184 | ### `fullscreen.exit()` 185 | Exits Fullscreen Mode 186 | 187 | ```javascript 188 | controller.fullscreen.exit(); 189 | ``` 190 | 191 | ## jQuery Support 192 | 193 | YTV may be used as a jQuery plugin in the following fashion: 194 | 195 | ```javascript 196 | $('#frame').ytv({opts}); 197 | ``` 198 | 199 | 200 | 201 | ## Licensing 202 | **MIT Licensing** 203 |

Copyright (c) 2013 Jacob Kelley

204 |

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

205 |

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

206 |

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

207 | -------------------------------------------------------------------------------- /src/ie8/es5-shim.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * https://github.com/es-shims/es5-shim 3 | * @license es5-shim Copyright 2009-2015 by contributors, MIT License 4 | * see https://github.com/es-shims/es5-shim/blob/v4.1.7/LICENSE 5 | */ 6 | (function(t,e){"use strict";if(typeof define==="function"&&define.amd){define(e)}else if(typeof exports==="object"){module.exports=e()}else{t.returnExports=e()}})(this,function(){var t=Array.prototype;var e=Object.prototype;var r=Function.prototype;var n=String.prototype;var i=Number.prototype;var a=t.slice;var o=t.splice;var u=t.push;var l=t.unshift;var f=t.concat;var s=r.call;var c=e.toString;var p=Array.isArray||function yt(t){return c.call(t)==="[object Array]"};var h=typeof Symbol==="function"&&typeof Symbol.toStringTag==="symbol";var v;var g=Function.prototype.toString,y=function dt(t){try{g.call(t);return true}catch(e){return false}},d="[object Function]",m="[object GeneratorFunction]";v=function mt(t){if(typeof t!=="function"){return false}if(h){return y(t)}var e=c.call(t);return e===d||e===m};var b;var w=RegExp.prototype.exec,T=function bt(t){try{w.call(t);return true}catch(e){return false}},x="[object RegExp]";b=function wt(t){if(typeof t!=="object"){return false}return h?T(t):c.call(t)===x};var O;var j=String.prototype.valueOf,S=function Tt(t){try{j.call(t);return true}catch(e){return false}},N="[object String]";O=function xt(t){if(typeof t==="string"){return true}if(typeof t!=="object"){return false}return h?S(t):c.call(t)===N};var E=function Ot(t){var e=c.call(t);var r=e==="[object Arguments]";if(!r){r=!p(t)&&t!==null&&typeof t==="object"&&typeof t.length==="number"&&t.length>=0&&v(t.callee)}return r};var I=function(t){var e=Object.defineProperty&&function(){try{var t={};Object.defineProperty(t,"x",{enumerable:false,value:t});for(var e in t){return false}return t.x===t}catch(r){return false}}();var r;if(e){r=function(t,e,r,n){if(!n&&e in t){return}Object.defineProperty(t,e,{configurable:true,enumerable:false,writable:true,value:r})}}else{r=function(t,e,r,n){if(!n&&e in t){return}t[e]=r}}return function n(e,i,a){for(var o in i){if(t.call(i,o)){r(e,o,i[o],a)}}}}(e.hasOwnProperty);var D=function jt(t){var e=typeof t;return t===null||e!=="object"&&e!=="function"};var M={ToInteger:function St(t){var e=+t;if(e!==e){e=0}else if(e!==0&&e!==1/0&&e!==-(1/0)){e=(e>0||-1)*Math.floor(Math.abs(e))}return e},ToPrimitive:function Nt(t){var e,r,n;if(D(t)){return t}r=t.valueOf;if(v(r)){e=r.call(t);if(D(e)){return e}}n=t.toString;if(v(n)){e=n.call(t);if(D(e)){return e}}throw new TypeError},ToObject:function(t){if(t==null){throw new TypeError("can't convert "+t+" to object")}return Object(t)},ToUint32:function Et(t){return t>>>0}};var k=function It(){};I(r,{bind:function Dt(t){var e=this;if(!v(e)){throw new TypeError("Function.prototype.bind called on incompatible "+e)}var r=a.call(arguments,1);var n;var i=function(){if(this instanceof n){var i=e.apply(this,f.call(r,a.call(arguments)));if(Object(i)===i){return i}return this}else{return e.apply(t,f.call(r,a.call(arguments)))}};var o=Math.max(0,e.length-r.length);var u=[];for(var l=0;l0&&typeof e!=="number"){r=a.call(arguments);if(r.length<2){r.push(this.length-t)}else{r[1]=M.ToInteger(e)}}return o.apply(this,r)}},!R);var U=[].unshift(0)!==1;I(t,{unshift:function(){l.apply(this,arguments);return this.length}},U);I(Array,{isArray:p});var C=Object("a");var P=C[0]!=="a"||!(0 in C);var Z=function At(t){var e=true;var r=true;if(t){t.call("foo",function(t,r,n){if(typeof n!=="object"){e=false}});t.call([1],function(){"use strict";r=typeof this==="string"},"x")}return!!t&&e&&r};I(t,{forEach:function Ft(t){var e=M.ToObject(this);var r=P&&O(this)?this.split(""):e;var n=-1;var i=r.length>>>0;var a;if(arguments.length>1){a=arguments[1]}if(!v(t)){throw new TypeError("Array.prototype.forEach callback must be a function")}while(++n>>0;var i=Array(n);var a;if(arguments.length>1){a=arguments[1]}if(!v(t)){throw new TypeError("Array.prototype.map callback must be a function")}for(var o=0;o>>0;var i=[];var a;var o;if(arguments.length>1){o=arguments[1]}if(!v(t)){throw new TypeError("Array.prototype.filter callback must be a function")}for(var u=0;u>>0;var i;if(arguments.length>1){i=arguments[1]}if(!v(t)){throw new TypeError("Array.prototype.every callback must be a function")}for(var a=0;a>>0;var i;if(arguments.length>1){i=arguments[1]}if(!v(t)){throw new TypeError("Array.prototype.some callback must be a function")}for(var a=0;a>>0;if(!v(t)){throw new TypeError("Array.prototype.reduce callback must be a function")}if(n===0&&arguments.length===1){throw new TypeError("reduce of empty array with no initial value")}var i=0;var a;if(arguments.length>=2){a=arguments[1]}else{do{if(i in r){a=r[i++];break}if(++i>=n){throw new TypeError("reduce of empty array with no initial value")}}while(true)}for(;i>>0;if(!v(t)){throw new TypeError("Array.prototype.reduceRight callback must be a function")}if(n===0&&arguments.length===1){throw new TypeError("reduceRight of empty array with no initial value")}var i;var a=n-1;if(arguments.length>=2){i=arguments[1]}else{do{if(a in r){i=r[a--];break}if(--a<0){throw new TypeError("reduceRight of empty array with no initial value")}}while(true)}if(a<0){return i}do{if(a in r){i=t(i,r[a],a,e)}}while(a--);return i}},!z);var $=Array.prototype.indexOf&&[0,1].indexOf(1,2)!==-1;I(t,{indexOf:function zt(t){var e=P&&O(this)?this.split(""):M.ToObject(this);var r=e.length>>>0;if(r===0){return-1}var n=0;if(arguments.length>1){n=M.ToInteger(arguments[1])}n=n>=0?n:Math.max(0,r+n);for(;n>>0;if(r===0){return-1}var n=r-1;if(arguments.length>1){n=Math.min(n,M.ToInteger(arguments[1]))}n=n>=0?n:r-Math.abs(n);for(;n>=0;n--){if(n in e&&t===e[n]){return n}}return-1}},B);var G=!{toString:null}.propertyIsEnumerable("toString"),H=function(){}.propertyIsEnumerable("prototype"),L=!A("x","0"),X=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],Y=X.length;I(Object,{keys:function Bt(t){var e=v(t),r=E(t),n=t!==null&&typeof t==="object",i=n&&O(t);if(!n&&!e&&!r){throw new TypeError("Object.keys called on a non-object")}var a=[];var o=H&&e;if(i&&L||r){for(var u=0;u9999?"+":"")+("00000"+Math.abs(n)).slice(0<=n&&n<=9999?-4:-6);e=t.length;while(e--){r=t[e];if(r<10){t[e]="0"+r}}return n+"-"+t.slice(0,2).join("-")+"T"+t.slice(2).join(":")+"."+("000"+this.getUTCMilliseconds()).slice(-3)+"Z"}},W);var _=function(){try{return Date.prototype.toJSON&&new Date(NaN).toJSON()===null&&new Date(Q).toJSON().indexOf(V)!==-1&&Date.prototype.toJSON.call({toISOString:function(){return true}})}catch(t){return false}}();if(!_){Date.prototype.toJSON=function Lt(t){var e=Object(this);var r=M.ToPrimitive(e);if(typeof r==="number"&&!isFinite(r)){return null}var n=e.toISOString;if(!v(n)){throw new TypeError("toISOString property is not callable")}return n.call(e)}}var tt=Date.parse("+033658-09-27T01:46:40.000Z")===1e15;var et=!isNaN(Date.parse("2012-04-04T24:00:00.500Z"))||!isNaN(Date.parse("2012-11-31T23:59:59.000Z"))||!isNaN(Date.parse("2012-12-31T23:59:60.000Z"));var rt=isNaN(Date.parse("2000-01-01T00:00:00.000Z"));if(!Date.parse||rt||et||!tt){Date=function(t){var e=function l(r,n,i,a,o,u,f){var s=arguments.length;var c;if(this instanceof t){c=s===1&&String(r)===r?new t(e.parse(r)):s>=7?new t(r,n,i,a,o,u,f):s>=6?new t(r,n,i,a,o,u):s>=5?new t(r,n,i,a,o):s>=4?new t(r,n,i,a):s>=3?new t(r,n,i):s>=2?new t(r,n):s>=1?new t(r):new t}else{c=t.apply(this,arguments)}I(c,{constructor:e},true);return c};var r=new RegExp("^"+"(\\d{4}|[+-]\\d{6})"+"(?:-(\\d{2})"+"(?:-(\\d{2})"+"(?:"+"T(\\d{2})"+":(\\d{2})"+"(?:"+":(\\d{2})"+"(?:(\\.\\d{1,}))?"+")?"+"("+"Z|"+"(?:"+"([-+])"+"(\\d{2})"+":(\\d{2})"+")"+")?)?)?)?"+"$");var n=[0,31,59,90,120,151,181,212,243,273,304,334,365];var i=function f(t,e){var r=e>1?1:0;return n[e]+Math.floor((t-1969+r)/4)-Math.floor((t-1901+r)/100)+Math.floor((t-1601+r)/400)+365*(t-1970)};var a=function s(e){return Number(new t(1970,0,1,0,0,0,e))};for(var o in t){if(A(t,o)){e[o]=t[o]}}I(e,{now:t.now,UTC:t.UTC},true);e.prototype=t.prototype;I(e.prototype,{constructor:e},true);var u=function c(e){var n=r.exec(e);if(n){var o=Number(n[1]),u=Number(n[2]||1)-1,l=Number(n[3]||1)-1,f=Number(n[4]||0),s=Number(n[5]||0),c=Number(n[6]||0),p=Math.floor(Number(n[7]||0)*1e3),h=Boolean(n[4]&&!n[8]),v=n[9]==="-"?1:-1,g=Number(n[10]||0),y=Number(n[11]||0),d;if(f<(s>0||c>0||p>0?24:25)&&s<60&&c<60&&p<1e3&&u>-1&&u<12&&g<24&&y<60&&l>-1&&l=0){r+=it.data[e];it.data[e]=Math.floor(r/t);r=r%t*it.base}},numToString:function Kt(){var t=it.size;var e="";while(--t>=0){if(e!==""||t===0||it.data[t]!==0){var r=String(it.data[t]);if(e===""){e=r}else{e+="0000000".slice(0,7-r.length)+r}}}return e},pow:function Qt(t,e,r){return e===0?r:e%2===1?Qt(t,e-1,r*t):Qt(t*t,e/2,r)},log:function Vt(t){var e=0;var r=t;while(r>=4096){e+=12;r/=4096}while(r>=2){e+=1;r/=2}return e}};I(i,{toFixed:function Wt(t){var e,r,n,i,a,o,u,l;e=Number(t);e=e!==e?0:Math.floor(e);if(e<0||e>20){throw new RangeError("Number.toFixed called with invalid number of decimals")}r=Number(this);if(r!==r){return"NaN"}if(r<=-1e21||r>=1e21){return String(r)}n="";if(r<0){n="-";r=-r}i="0";if(r>1e-21){a=it.log(r*it.pow(2,69,1))-69;o=a<0?r*it.pow(2,-a,1):r/it.pow(2,a,1);o*=4503599627370496;a=52-a;if(a>0){it.multiply(0,o);u=e;while(u>=7){it.multiply(1e7,0);u-=7}it.multiply(it.pow(10,u,1),0);u=a-1;while(u>=23){it.divide(1<<23);u-=23}it.divide(1<0){l=i.length;if(l<=e){i=n+"0.0000000000000000000".slice(0,e-l+2)+i}else{i=n+i.slice(0,l-e)+"."+i.slice(l-e)}}else{i=n+i}return i}},nt);var at=n.split;if("ab".split(/(?:ab)*/).length!==2||".".split(/(.?)(.?)/).length!==4||"tesst".split(/(s)*/)[1]==="t"||"test".split(/(?:)/,-1).length!==4||"".split(/.?/).length||".".split(/()()/).length>1){(function(){var t=typeof/()??/.exec("")[1]==="undefined";n.split=function(e,r){var n=this;if(typeof e==="undefined"&&r===0){return[]}if(!b(e)){return at.call(this,e,r)}var i=[];var a=(e.ignoreCase?"i":"")+(e.multiline?"m":"")+(e.extended?"x":"")+(e.sticky?"y":""),o=0,l,f,s,c;var p=new RegExp(e.source,a+"g");n+="";if(!t){l=new RegExp("^"+p.source+"$(?!\\s)",a)}var h=typeof r==="undefined"?-1>>>0:M.ToUint32(r);f=p.exec(n);while(f){s=f.index+f[0].length;if(s>o){i.push(n.slice(o,f.index));if(!t&&f.length>1){f[0].replace(l,function(){for(var t=1;t1&&f.index=h){break}}if(p.lastIndex===f.index){p.lastIndex++}f=p.exec(n)}if(o===n.length){if(c||!p.test("")){i.push("")}}else{i.push(n.slice(o))}return i.length>h?i.slice(0,h):i}})()}else if("0".split(void 0,0).length){n.split=function _t(t,e){if(typeof t==="undefined"&&e===0){return[]}return at.call(this,t,e)}}var ot=n.replace;var ut=function(){var t=[];"x".replace(/x(.)?/g,function(e,r){t.push(r)});return t.length===1&&typeof t[0]==="undefined"}();if(!ut){n.replace=function te(t,e){var r=v(e);var n=b(t)&&/\)[*?]/.test(t.source);if(!r||!n){return ot.call(this,t,e)}else{var i=function(r){var n=arguments.length;var i=t.lastIndex;t.lastIndex=0;var a=t.exec(r)||[];t.lastIndex=i;a.push(arguments[n-2],arguments[n-1]);return e.apply(this,a)};return ot.call(this,t,i)}}}var lt=n.substr;var ft="".substr&&"0b".substr(-1)!=="b";I(n,{substr:function ee(t,e){var r=t;if(t<0){r=Math.max(this.length+t,0)}return lt.call(this,r,e)}},ft);var st=" \n \f\r \xa0\u1680\u180e\u2000\u2001\u2002\u2003"+"\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028"+"\u2029\ufeff";var ct="\u200b";var pt="["+st+"]";var ht=new RegExp("^"+pt+pt+"*");var vt=new RegExp(pt+pt+"*$");var gt=n.trim&&(st.trim()||!ct.trim());I(n,{trim:function re(){if(typeof this==="undefined"||this===null){throw new TypeError("can't convert "+this+" to object")}return String(this).replace(ht,"").replace(vt,"")}},gt);if(parseInt(st+"08")!==8||parseInt(st+"0x16")!==22){parseInt=function(t){var e=/^0[xX]/;return function r(n,i){var a=String(n).trim();var o=Number(i)||(e.test(a)?16:10);return t(a,o)}}(parseInt)}}); 7 | //# sourceMappingURL=es5-shim.map 8 | -------------------------------------------------------------------------------- /demos/assets/ie8/es5-shim.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * https://github.com/es-shims/es5-shim 3 | * @license es5-shim Copyright 2009-2015 by contributors, MIT License 4 | * see https://github.com/es-shims/es5-shim/blob/v4.1.7/LICENSE 5 | */ 6 | (function(t,e){"use strict";if(typeof define==="function"&&define.amd){define(e)}else if(typeof exports==="object"){module.exports=e()}else{t.returnExports=e()}})(this,function(){var t=Array.prototype;var e=Object.prototype;var r=Function.prototype;var n=String.prototype;var i=Number.prototype;var a=t.slice;var o=t.splice;var u=t.push;var l=t.unshift;var f=t.concat;var s=r.call;var c=e.toString;var p=Array.isArray||function yt(t){return c.call(t)==="[object Array]"};var h=typeof Symbol==="function"&&typeof Symbol.toStringTag==="symbol";var v;var g=Function.prototype.toString,y=function dt(t){try{g.call(t);return true}catch(e){return false}},d="[object Function]",m="[object GeneratorFunction]";v=function mt(t){if(typeof t!=="function"){return false}if(h){return y(t)}var e=c.call(t);return e===d||e===m};var b;var w=RegExp.prototype.exec,T=function bt(t){try{w.call(t);return true}catch(e){return false}},x="[object RegExp]";b=function wt(t){if(typeof t!=="object"){return false}return h?T(t):c.call(t)===x};var O;var j=String.prototype.valueOf,S=function Tt(t){try{j.call(t);return true}catch(e){return false}},N="[object String]";O=function xt(t){if(typeof t==="string"){return true}if(typeof t!=="object"){return false}return h?S(t):c.call(t)===N};var E=function Ot(t){var e=c.call(t);var r=e==="[object Arguments]";if(!r){r=!p(t)&&t!==null&&typeof t==="object"&&typeof t.length==="number"&&t.length>=0&&v(t.callee)}return r};var I=function(t){var e=Object.defineProperty&&function(){try{var t={};Object.defineProperty(t,"x",{enumerable:false,value:t});for(var e in t){return false}return t.x===t}catch(r){return false}}();var r;if(e){r=function(t,e,r,n){if(!n&&e in t){return}Object.defineProperty(t,e,{configurable:true,enumerable:false,writable:true,value:r})}}else{r=function(t,e,r,n){if(!n&&e in t){return}t[e]=r}}return function n(e,i,a){for(var o in i){if(t.call(i,o)){r(e,o,i[o],a)}}}}(e.hasOwnProperty);var D=function jt(t){var e=typeof t;return t===null||e!=="object"&&e!=="function"};var M={ToInteger:function St(t){var e=+t;if(e!==e){e=0}else if(e!==0&&e!==1/0&&e!==-(1/0)){e=(e>0||-1)*Math.floor(Math.abs(e))}return e},ToPrimitive:function Nt(t){var e,r,n;if(D(t)){return t}r=t.valueOf;if(v(r)){e=r.call(t);if(D(e)){return e}}n=t.toString;if(v(n)){e=n.call(t);if(D(e)){return e}}throw new TypeError},ToObject:function(t){if(t==null){throw new TypeError("can't convert "+t+" to object")}return Object(t)},ToUint32:function Et(t){return t>>>0}};var k=function It(){};I(r,{bind:function Dt(t){var e=this;if(!v(e)){throw new TypeError("Function.prototype.bind called on incompatible "+e)}var r=a.call(arguments,1);var n;var i=function(){if(this instanceof n){var i=e.apply(this,f.call(r,a.call(arguments)));if(Object(i)===i){return i}return this}else{return e.apply(t,f.call(r,a.call(arguments)))}};var o=Math.max(0,e.length-r.length);var u=[];for(var l=0;l0&&typeof e!=="number"){r=a.call(arguments);if(r.length<2){r.push(this.length-t)}else{r[1]=M.ToInteger(e)}}return o.apply(this,r)}},!R);var U=[].unshift(0)!==1;I(t,{unshift:function(){l.apply(this,arguments);return this.length}},U);I(Array,{isArray:p});var C=Object("a");var P=C[0]!=="a"||!(0 in C);var Z=function At(t){var e=true;var r=true;if(t){t.call("foo",function(t,r,n){if(typeof n!=="object"){e=false}});t.call([1],function(){"use strict";r=typeof this==="string"},"x")}return!!t&&e&&r};I(t,{forEach:function Ft(t){var e=M.ToObject(this);var r=P&&O(this)?this.split(""):e;var n=-1;var i=r.length>>>0;var a;if(arguments.length>1){a=arguments[1]}if(!v(t)){throw new TypeError("Array.prototype.forEach callback must be a function")}while(++n>>0;var i=Array(n);var a;if(arguments.length>1){a=arguments[1]}if(!v(t)){throw new TypeError("Array.prototype.map callback must be a function")}for(var o=0;o>>0;var i=[];var a;var o;if(arguments.length>1){o=arguments[1]}if(!v(t)){throw new TypeError("Array.prototype.filter callback must be a function")}for(var u=0;u>>0;var i;if(arguments.length>1){i=arguments[1]}if(!v(t)){throw new TypeError("Array.prototype.every callback must be a function")}for(var a=0;a>>0;var i;if(arguments.length>1){i=arguments[1]}if(!v(t)){throw new TypeError("Array.prototype.some callback must be a function")}for(var a=0;a>>0;if(!v(t)){throw new TypeError("Array.prototype.reduce callback must be a function")}if(n===0&&arguments.length===1){throw new TypeError("reduce of empty array with no initial value")}var i=0;var a;if(arguments.length>=2){a=arguments[1]}else{do{if(i in r){a=r[i++];break}if(++i>=n){throw new TypeError("reduce of empty array with no initial value")}}while(true)}for(;i>>0;if(!v(t)){throw new TypeError("Array.prototype.reduceRight callback must be a function")}if(n===0&&arguments.length===1){throw new TypeError("reduceRight of empty array with no initial value")}var i;var a=n-1;if(arguments.length>=2){i=arguments[1]}else{do{if(a in r){i=r[a--];break}if(--a<0){throw new TypeError("reduceRight of empty array with no initial value")}}while(true)}if(a<0){return i}do{if(a in r){i=t(i,r[a],a,e)}}while(a--);return i}},!z);var $=Array.prototype.indexOf&&[0,1].indexOf(1,2)!==-1;I(t,{indexOf:function zt(t){var e=P&&O(this)?this.split(""):M.ToObject(this);var r=e.length>>>0;if(r===0){return-1}var n=0;if(arguments.length>1){n=M.ToInteger(arguments[1])}n=n>=0?n:Math.max(0,r+n);for(;n>>0;if(r===0){return-1}var n=r-1;if(arguments.length>1){n=Math.min(n,M.ToInteger(arguments[1]))}n=n>=0?n:r-Math.abs(n);for(;n>=0;n--){if(n in e&&t===e[n]){return n}}return-1}},B);var G=!{toString:null}.propertyIsEnumerable("toString"),H=function(){}.propertyIsEnumerable("prototype"),L=!A("x","0"),X=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],Y=X.length;I(Object,{keys:function Bt(t){var e=v(t),r=E(t),n=t!==null&&typeof t==="object",i=n&&O(t);if(!n&&!e&&!r){throw new TypeError("Object.keys called on a non-object")}var a=[];var o=H&&e;if(i&&L||r){for(var u=0;u9999?"+":"")+("00000"+Math.abs(n)).slice(0<=n&&n<=9999?-4:-6);e=t.length;while(e--){r=t[e];if(r<10){t[e]="0"+r}}return n+"-"+t.slice(0,2).join("-")+"T"+t.slice(2).join(":")+"."+("000"+this.getUTCMilliseconds()).slice(-3)+"Z"}},W);var _=function(){try{return Date.prototype.toJSON&&new Date(NaN).toJSON()===null&&new Date(Q).toJSON().indexOf(V)!==-1&&Date.prototype.toJSON.call({toISOString:function(){return true}})}catch(t){return false}}();if(!_){Date.prototype.toJSON=function Lt(t){var e=Object(this);var r=M.ToPrimitive(e);if(typeof r==="number"&&!isFinite(r)){return null}var n=e.toISOString;if(!v(n)){throw new TypeError("toISOString property is not callable")}return n.call(e)}}var tt=Date.parse("+033658-09-27T01:46:40.000Z")===1e15;var et=!isNaN(Date.parse("2012-04-04T24:00:00.500Z"))||!isNaN(Date.parse("2012-11-31T23:59:59.000Z"))||!isNaN(Date.parse("2012-12-31T23:59:60.000Z"));var rt=isNaN(Date.parse("2000-01-01T00:00:00.000Z"));if(!Date.parse||rt||et||!tt){Date=function(t){var e=function l(r,n,i,a,o,u,f){var s=arguments.length;var c;if(this instanceof t){c=s===1&&String(r)===r?new t(e.parse(r)):s>=7?new t(r,n,i,a,o,u,f):s>=6?new t(r,n,i,a,o,u):s>=5?new t(r,n,i,a,o):s>=4?new t(r,n,i,a):s>=3?new t(r,n,i):s>=2?new t(r,n):s>=1?new t(r):new t}else{c=t.apply(this,arguments)}I(c,{constructor:e},true);return c};var r=new RegExp("^"+"(\\d{4}|[+-]\\d{6})"+"(?:-(\\d{2})"+"(?:-(\\d{2})"+"(?:"+"T(\\d{2})"+":(\\d{2})"+"(?:"+":(\\d{2})"+"(?:(\\.\\d{1,}))?"+")?"+"("+"Z|"+"(?:"+"([-+])"+"(\\d{2})"+":(\\d{2})"+")"+")?)?)?)?"+"$");var n=[0,31,59,90,120,151,181,212,243,273,304,334,365];var i=function f(t,e){var r=e>1?1:0;return n[e]+Math.floor((t-1969+r)/4)-Math.floor((t-1901+r)/100)+Math.floor((t-1601+r)/400)+365*(t-1970)};var a=function s(e){return Number(new t(1970,0,1,0,0,0,e))};for(var o in t){if(A(t,o)){e[o]=t[o]}}I(e,{now:t.now,UTC:t.UTC},true);e.prototype=t.prototype;I(e.prototype,{constructor:e},true);var u=function c(e){var n=r.exec(e);if(n){var o=Number(n[1]),u=Number(n[2]||1)-1,l=Number(n[3]||1)-1,f=Number(n[4]||0),s=Number(n[5]||0),c=Number(n[6]||0),p=Math.floor(Number(n[7]||0)*1e3),h=Boolean(n[4]&&!n[8]),v=n[9]==="-"?1:-1,g=Number(n[10]||0),y=Number(n[11]||0),d;if(f<(s>0||c>0||p>0?24:25)&&s<60&&c<60&&p<1e3&&u>-1&&u<12&&g<24&&y<60&&l>-1&&l=0){r+=it.data[e];it.data[e]=Math.floor(r/t);r=r%t*it.base}},numToString:function Kt(){var t=it.size;var e="";while(--t>=0){if(e!==""||t===0||it.data[t]!==0){var r=String(it.data[t]);if(e===""){e=r}else{e+="0000000".slice(0,7-r.length)+r}}}return e},pow:function Qt(t,e,r){return e===0?r:e%2===1?Qt(t,e-1,r*t):Qt(t*t,e/2,r)},log:function Vt(t){var e=0;var r=t;while(r>=4096){e+=12;r/=4096}while(r>=2){e+=1;r/=2}return e}};I(i,{toFixed:function Wt(t){var e,r,n,i,a,o,u,l;e=Number(t);e=e!==e?0:Math.floor(e);if(e<0||e>20){throw new RangeError("Number.toFixed called with invalid number of decimals")}r=Number(this);if(r!==r){return"NaN"}if(r<=-1e21||r>=1e21){return String(r)}n="";if(r<0){n="-";r=-r}i="0";if(r>1e-21){a=it.log(r*it.pow(2,69,1))-69;o=a<0?r*it.pow(2,-a,1):r/it.pow(2,a,1);o*=4503599627370496;a=52-a;if(a>0){it.multiply(0,o);u=e;while(u>=7){it.multiply(1e7,0);u-=7}it.multiply(it.pow(10,u,1),0);u=a-1;while(u>=23){it.divide(1<<23);u-=23}it.divide(1<0){l=i.length;if(l<=e){i=n+"0.0000000000000000000".slice(0,e-l+2)+i}else{i=n+i.slice(0,l-e)+"."+i.slice(l-e)}}else{i=n+i}return i}},nt);var at=n.split;if("ab".split(/(?:ab)*/).length!==2||".".split(/(.?)(.?)/).length!==4||"tesst".split(/(s)*/)[1]==="t"||"test".split(/(?:)/,-1).length!==4||"".split(/.?/).length||".".split(/()()/).length>1){(function(){var t=typeof/()??/.exec("")[1]==="undefined";n.split=function(e,r){var n=this;if(typeof e==="undefined"&&r===0){return[]}if(!b(e)){return at.call(this,e,r)}var i=[];var a=(e.ignoreCase?"i":"")+(e.multiline?"m":"")+(e.extended?"x":"")+(e.sticky?"y":""),o=0,l,f,s,c;var p=new RegExp(e.source,a+"g");n+="";if(!t){l=new RegExp("^"+p.source+"$(?!\\s)",a)}var h=typeof r==="undefined"?-1>>>0:M.ToUint32(r);f=p.exec(n);while(f){s=f.index+f[0].length;if(s>o){i.push(n.slice(o,f.index));if(!t&&f.length>1){f[0].replace(l,function(){for(var t=1;t1&&f.index=h){break}}if(p.lastIndex===f.index){p.lastIndex++}f=p.exec(n)}if(o===n.length){if(c||!p.test("")){i.push("")}}else{i.push(n.slice(o))}return i.length>h?i.slice(0,h):i}})()}else if("0".split(void 0,0).length){n.split=function _t(t,e){if(typeof t==="undefined"&&e===0){return[]}return at.call(this,t,e)}}var ot=n.replace;var ut=function(){var t=[];"x".replace(/x(.)?/g,function(e,r){t.push(r)});return t.length===1&&typeof t[0]==="undefined"}();if(!ut){n.replace=function te(t,e){var r=v(e);var n=b(t)&&/\)[*?]/.test(t.source);if(!r||!n){return ot.call(this,t,e)}else{var i=function(r){var n=arguments.length;var i=t.lastIndex;t.lastIndex=0;var a=t.exec(r)||[];t.lastIndex=i;a.push(arguments[n-2],arguments[n-1]);return e.apply(this,a)};return ot.call(this,t,i)}}}var lt=n.substr;var ft="".substr&&"0b".substr(-1)!=="b";I(n,{substr:function ee(t,e){var r=t;if(t<0){r=Math.max(this.length+t,0)}return lt.call(this,r,e)}},ft);var st=" \n \f\r \xa0\u1680\u180e\u2000\u2001\u2002\u2003"+"\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028"+"\u2029\ufeff";var ct="\u200b";var pt="["+st+"]";var ht=new RegExp("^"+pt+pt+"*");var vt=new RegExp(pt+pt+"*$");var gt=n.trim&&(st.trim()||!ct.trim());I(n,{trim:function re(){if(typeof this==="undefined"||this===null){throw new TypeError("can't convert "+this+" to object")}return String(this).replace(ht,"").replace(vt,"")}},gt);if(parseInt(st+"08")!==8||parseInt(st+"0x16")!==22){parseInt=function(t){var e=/^0[xX]/;return function r(n,i){var a=String(n).trim();var o=Number(i)||(e.test(a)?16:10);return t(a,o)}}(parseInt)}}); 7 | //# sourceMappingURL=es5-shim.map 8 | -------------------------------------------------------------------------------- /demos/assets/bootstrap.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.3.2 (http://getbootstrap.com) 3 | * Copyright 2011-2015 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */ 6 | 7 | /*! 8 | * Generated using the Bootstrap Customizer (http://getbootstrap.com/customize/?id=184acbd8ae477c221405) 9 | * Config saved to config.json and https://gist.github.com/184acbd8ae477c221405 10 | *//*! normalize.css v3.0.2 | MIT License | git.io/normalize */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,*:before,*:after{background:transparent !important;color:#000 !important;-webkit-box-shadow:none !important;box-shadow:none !important;text-shadow:none !important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="#"]:after,a[href^="javascript:"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}select{background:#fff !important}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000 !important}.label{border:1px solid #000}.table{border-collapse:collapse !important}.table td,.table th{background-color:#fff !important}.table-bordered th,.table-bordered td{border:1px solid #ddd !important}}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}*:before,*:after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:hover,a:focus{color:#23527c;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}.container{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}.row{margin-left:-15px;margin-right:-15px}.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12{position:relative;min-height:1px;padding-left:15px;padding-right:15px}.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}.clearfix:before,.clearfix:after,.container:before,.container:after,.container-fluid:before,.container-fluid:after,.row:before,.row:after{content:" ";display:table}.clearfix:after,.container:after,.container-fluid:after,.row:after{clear:both}.center-block{display:block;margin-left:auto;margin-right:auto}.pull-right{float:right !important}.pull-left{float:left !important}.hide{display:none !important}.show{display:block !important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none !important;visibility:hidden !important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-xs,.visible-sm,.visible-md,.visible-lg{display:none !important}.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block{display:none !important}@media (max-width:767px){.visible-xs{display:block !important}table.visible-xs{display:table}tr.visible-xs{display:table-row !important}th.visible-xs,td.visible-xs{display:table-cell !important}}@media (max-width:767px){.visible-xs-block{display:block !important}}@media (max-width:767px){.visible-xs-inline{display:inline !important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block !important}table.visible-sm{display:table}tr.visible-sm{display:table-row !important}th.visible-sm,td.visible-sm{display:table-cell !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block !important}table.visible-md{display:table}tr.visible-md{display:table-row !important}th.visible-md,td.visible-md{display:table-cell !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block !important}}@media (min-width:1200px){.visible-lg{display:block !important}table.visible-lg{display:table}tr.visible-lg{display:table-row !important}th.visible-lg,td.visible-lg{display:table-cell !important}}@media (min-width:1200px){.visible-lg-block{display:block !important}}@media (min-width:1200px){.visible-lg-inline{display:inline !important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block !important}}@media (max-width:767px){.hidden-xs{display:none !important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none !important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none !important}}@media (min-width:1200px){.hidden-lg{display:none !important}}.visible-print{display:none !important}@media print{.visible-print{display:block !important}table.visible-print{display:table}tr.visible-print{display:table-row !important}th.visible-print,td.visible-print{display:table-cell !important}}.visible-print-block{display:none !important}@media print{.visible-print-block{display:block !important}}.visible-print-inline{display:none !important}@media print{.visible-print-inline{display:inline !important}}.visible-print-inline-block{display:none !important}@media print{.visible-print-inline-block{display:inline-block !important}}@media print{.hidden-print{display:none !important}} -------------------------------------------------------------------------------- /src/ytv.js: -------------------------------------------------------------------------------- 1 | /* 2 | * YouTube TV 3 | * 4 | * Copyright 2013, Jacob Kelley - http://jakiestfu.com/ 5 | * Released under the MIT Licence 6 | * http://opensource.org/licenses/MIT 7 | * 8 | * Github: 9 | * Version: 3.0.5 10 | */ 11 | /*jslint browser: true, undef:true, unused:true, laxbreak:true, loopfunc:true*/ 12 | /*global define, module, ender */ 13 | 14 | (function(win, doc) { 15 | 'use strict'; 16 | var apiKey = 'YOUR_API_KEY_HERE'; 17 | var YTV = YTV || function(id, opts){ 18 | 19 | var noop = function(){}, 20 | settings = { 21 | apiKey: apiKey, 22 | element: null, 23 | user: null, 24 | channelId: null, 25 | fullscreen: false, 26 | accent: '#fff', 27 | controls: true, 28 | annotations: false, 29 | autoplay: false, 30 | chainVideos: true, 31 | browsePlaylists: false, 32 | playerTheme: 'dark', 33 | listTheme: 'dark', 34 | responsive: false, 35 | playId:'', 36 | sortList: false, 37 | reverseList: false, 38 | shuffleList: false, 39 | wmode: 'opaque', 40 | events: { 41 | videoReady: noop, 42 | stateChange: noop 43 | } 44 | }, 45 | 46 | cache = { 47 | data: {}, 48 | remove: function (url) { 49 | delete cache.data[url]; 50 | }, 51 | exist: function (url) { 52 | return cache.data.hasOwnProperty(url) && cache.data[url] !== null; 53 | }, 54 | get: function (url) { 55 | return cache.data[url]; 56 | }, 57 | set: function (url, data) { 58 | cache.remove(url); 59 | cache.data[url] = data; 60 | } 61 | }, 62 | utils = { 63 | events: { 64 | addEvent: function addEvent(element, eventName, func) { 65 | if (element.addEventListener) { 66 | return element.addEventListener(eventName, func, false); 67 | } else if (element.attachEvent) { 68 | return element.attachEvent("on" + eventName, func); 69 | } 70 | }, 71 | removeEvent: function addEvent(element, eventName, func) { 72 | if (element.addEventListener) { 73 | return element.removeEventListener(eventName, func, false); 74 | } else if (element.attachEvent) { 75 | return element.detachEvent("on" + eventName, func); 76 | } 77 | }, 78 | prevent: function(e) { 79 | if (e.preventDefault) { 80 | e.preventDefault(); 81 | } else { 82 | e.returnValue = false; 83 | } 84 | } 85 | }, 86 | addCSS: function(css){ 87 | var head = doc.getElementsByTagName('head')[0], 88 | style = doc.createElement('style'); 89 | style.type = 'text/css'; 90 | if (style.styleSheet){ 91 | style.styleSheet.cssText = css; 92 | } else { 93 | style.appendChild(doc.createTextNode(css)); 94 | } 95 | head.appendChild(style); 96 | }, 97 | addCommas: function(str){ 98 | var x = str.split('.'), 99 | x1 = x[0], 100 | x2 = x.length > 1 ? '.' + x[1] : '', 101 | rgx = /(\d+)(\d{3})/; 102 | while (rgx.test(x1)) { 103 | x1 = x1.replace(rgx, '$1' + ',' + '$2'); 104 | } 105 | return x1 + x2; 106 | }, 107 | parentUntil: function(el, attr) { 108 | while (el.parentNode) { 109 | if (el.getAttribute && el.getAttribute(attr)){ 110 | return el; 111 | } 112 | el = el.parentNode; 113 | } 114 | return null; 115 | }, 116 | ajax: { 117 | get: function(url, fn){ 118 | if (cache.exist(url)) { 119 | fn.call(this, JSON.parse(cache.get(url))); 120 | } else { 121 | var handle; 122 | if (win.XDomainRequest && !(navigator.appVersion.indexOf("MSIE 8")==-1 || navigator.appVersion.indexOf("MSIE 9")==-1)) { // CORS for IE8,9 123 | handle = new XDomainRequest(); 124 | handle.onload = function(){ 125 | cache.set(url, handle.responseText); 126 | fn.call(this, JSON.parse(handle.responseText)); 127 | if (Object.prototype.hasOwnProperty.call(JSON.parse(handle.responseText), 'error')){ 128 | cache.remove(url); 129 | var e = JSON.parse(handle.responseText); 130 | console.log('Youtube-TV Error: Youtube API Response: '+e.error.errors[0].reason+'\n'+ 'Details: '+e.error.errors[0].message); 131 | } 132 | }; 133 | } else if (win.XMLHttpRequest){ // Modern Browsers 134 | handle = new XMLHttpRequest(); 135 | } 136 | handle.onreadystatechange = function(){ 137 | if (handle.readyState === 4 && handle.status === 200){ 138 | cache.set(url, handle.responseText); 139 | fn.call(this, JSON.parse(handle.responseText)); 140 | } else if (handle.readyState === 4){ 141 | var e = JSON.parse(handle.responseText); 142 | console.log('Youtube-TV Error: Youtube API Response: '+e.error.errors[0].reason+'\n'+ 'Details: '+e.error.errors[0].message); 143 | } 144 | }; 145 | handle.open("GET",url,true); 146 | handle.send(); 147 | } 148 | } 149 | }, 150 | endpoints: { 151 | base: 'https://www.googleapis.com/youtube/v3/', 152 | userInfo: function(){ 153 | return utils.endpoints.base+'channels?'+settings.cid+'&key='+apiKey+'&part=snippet,contentDetails,statistics'; 154 | }, 155 | playlistInfo: function(pid){ 156 | return utils.endpoints.base+'playlists?id='+pid+'&key='+apiKey+'&maxResults=50&part=snippet'; 157 | }, 158 | userPlaylists: function(){ 159 | return utils.endpoints.base+'playlists?channelId='+settings.channelId+'&key='+apiKey+'&maxResults=50&part=snippet'; 160 | }, 161 | playlistVids: function(){ 162 | return utils.endpoints.base+'playlistItems?playlistId='+settings.pid+'&key='+apiKey+'&maxResults=50&part=contentDetails'; 163 | }, 164 | videoInfo: function(){ 165 | return utils.endpoints.base+'videos?id='+settings.videoString+'&key='+apiKey+'&maxResults=50&part=snippet,contentDetails,status,statistics'; 166 | } 167 | }, 168 | deepExtend: function(destination, source) { 169 | var property; 170 | for (property in source) { 171 | if (source[property] && source[property].constructor && source[property].constructor === Object) { 172 | destination[property] = destination[property] || {}; 173 | utils.deepExtend(destination[property], source[property]); 174 | } else { 175 | destination[property] = source[property]; 176 | } 177 | } 178 | return destination; 179 | } 180 | }, 181 | prepare = { 182 | youtube: function(){ 183 | if(typeof YT=='undefined'){ 184 | var tag = doc.createElement('script'); 185 | tag.src = "https://www.youtube.com/iframe_api"; 186 | var firstScriptTag = doc.getElementsByTagName('script')[0]; 187 | firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); 188 | } 189 | }, 190 | build: function(){ 191 | if (settings.channelId){ 192 | settings.cid = 'id='+settings.channelId; 193 | } else if(settings.user){ 194 | settings.cid = 'forUsername='+settings.user; 195 | } 196 | settings.element.className = "ytv-canvas"; 197 | if(settings.fullscreen){ 198 | settings.element.className += " ytv-full"; 199 | } 200 | utils.addCSS( '#'+id+' .ytv-list .ytv-active a{border-left-color: '+(settings.accent)+';}' ); 201 | // Responsive CSS 202 | if(settings.responsive){ 203 | utils.addCSS('#'+id+' .ytv-video{' 204 | +'position: relative; padding-bottom: 39.4%; /* 16:9 of 70%*/' 205 | +'height: 0; width: 70%;' 206 | +'} #'+id+' .ytv-video iframe{' 207 | +'position: absolute; top: 0; left: 0;' 208 | +'} #'+id+' .ytv-list{' 209 | +'width: 30%;' 210 | +'} #'+id+' .ytv-playlist-open .ytv-arrow{' 211 | +'top: 0px;}' 212 | +'@media only screen and (max-width:992px) {' 213 | +'#'+id+' .ytv-list{' 214 | +'position: relative; display: block;' 215 | +'width: 0; padding-bottom: 40%;' 216 | +'left: auto; right: auto;' 217 | +'top: auto; width: 100%;' 218 | +'} #'+id+' .ytv-video{' 219 | +'position: relative; padding-bottom: 56.25%; /* 16:9 */' 220 | +'height: 0; position: relative;' 221 | +'display: block; left: auto;' 222 | +'right: auto; top: auto; width: 100%;' 223 | +'}}' 224 | ); 225 | } 226 | // Temp Scroll Bar fix 227 | if (settings.listTheme == 'dark'){ 228 | utils.addCSS( ' #'+id+'.ytv-canvas ::-webkit-scrollbar{border-left: 1px solid #000;}' 229 | + ' #'+id+'.ytv-canvas ::-webkit-scrollbar-thumb{background: rgba(255,255,255,0.2);}'); 230 | } 231 | // Optional Light List Theme 232 | if(settings.listTheme == 'light'){ 233 | utils.addCSS( ' #'+id+'.ytv-canvas{background: #ccc;}' 234 | + ' #'+id+'.ytv-canvas ::-webkit-scrollbar{border-left: 1px solid rgba(28,28,28,0.1);}' 235 | + ' #'+id+'.ytv-canvas ::-webkit-scrollbar-thumb{background: rgba(28,28,28,0.3);}' 236 | + ' #'+id+' .ytv-list .ytv-active a{background: rgba(0,0,0,0.2);}' 237 | + ' #'+id+' .ytv-list a{color: #282828; border-top: 1px solid rgba(0,0,0,0.1); border-bottom: 1px solid rgba(204,204,204,0.5);}' 238 | + ' #'+id+' .ytv-list a:hover, #'+id+' .ytv-list-header .ytv-playlists a:hover{ background: rgba(0,0,0,0.2);}' 239 | + ' #'+id+' .ytv-list a:active, #'+id+' .ytv-list-header .ytv-playlists a:active{ background: rgba(0,0,0,0.2);}' 240 | + ' #'+id+' .ytv-list .ytv-thumb-stroke{outline: 1px solid rgba(0,0,0,0.1);}' 241 | + ' #'+id+' .ytv-list .ytv-thumb{outline: 1px solid rgba(255,255,255,0.5);}' 242 | + ' #'+id+' .ytv-list-header{-webkit-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.2); -moz-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.2); box-shadow: 0 1px 2px rgba(255, 255, 255, 0.2);}' 243 | + ' #'+id+' .ytv-list-header a{background: rgba(0,0,0,0.2);}' 244 | + ' #'+id+' .ytv-playlists{background: #ccc;}' 245 | ); 246 | } 247 | }, 248 | userUploads: function(userInfo){ 249 | if (userInfo && userInfo.items.length > 0){ 250 | settings.pid = userInfo.items[0].contentDetails.relatedPlaylists.uploads; 251 | utils.ajax.get( utils.endpoints.playlistVids(), prepare.compileVideos ); 252 | } else console.log ('Youtube-TV Error: API returned no matches for: '+(settings.channelId ? settings.channelId : settings.user)+'\nPlease ensure it was entered correctly and in the appropriate field shown below. \nuser: \'username\' or channelId: \'UCxxxx...\''); 253 | }, 254 | selectedPlaylist: function(playlistInfo){ 255 | if (playlistInfo && playlistInfo.items.length > 0) { 256 | if (!settings.channelId && !settings.user){ 257 | settings.cid = ('id='+(settings.channelId = playlistInfo.items[0].snippet.channelId)); 258 | } 259 | settings.currentPlaylist = playlistInfo.items[0].snippet.title; 260 | settings.pid = playlistInfo.items[0].id; 261 | utils.ajax.get( utils.endpoints.playlistVids(), prepare.compileVideos ); 262 | } else console.log ('Youtube-TV Error: API returned no matches for playlist(s): '+settings.playlist); 263 | }, 264 | compileVideos: function(res){ 265 | if (res && res.items.length > 0){ 266 | var playlists = res.items, 267 | i; 268 | settings.videoString = ''; 269 | for(i=0; i 0){ 278 | var list = '
    ', 279 | playlists = res.items, 280 | i; 281 | for(i=0; i'; 288 | list += '
    '; 289 | list += ''+(data.title)+''; 290 | list += ''; 291 | } 292 | list += '
'; 293 | 294 | var lh = settings.element.getElementsByClassName('ytv-list-header')[0], 295 | headerLink = lh.children[0]; 296 | headerLink.href="#"; 297 | headerLink.target=""; 298 | headerLink.setAttribute('data-ytv-playlist-toggle', 'true'); 299 | settings.element.getElementsByClassName('ytv-list-header')[0].innerHTML += list; 300 | lh.className += ' ytv-has-playlists'; 301 | } else console.log ('Youtube-TV Error: Returned no playlists'); 302 | }, 303 | compileList: function(data){ 304 | if(data && data.items.length > 0){ 305 | utils.ajax.get( utils.endpoints.userInfo(), function(userInfo){ 306 | var list = '', 307 | user = { 308 | title: userInfo.items[0].snippet.title, 309 | url: '//youtube.com/channel/'+userInfo.items[0].id, 310 | thumb: userInfo.items[0].snippet.thumbnails['default'].url, 311 | summary: userInfo.items[0].snippet.description, 312 | subscribers: userInfo.items[0].statistics.subscriberCount, 313 | views: userInfo.items[0].statistics.viewCount 314 | }, 315 | videos = data.items, 316 | first = true, 317 | i; 318 | settings.channelId = userInfo.items[0].id; 319 | if(settings.currentPlaylist) user.title += ' · '+(settings.currentPlaylist); 320 | if (settings.sortList) videos.sort(function(a,b){if(a.snippet.publishedAt > b.snippet.publishedAt) return -1;if(a.snippet.publishedAt < b.snippet.publishedAt) return 1;return 0;}); 321 | if (settings.reverseList) videos.reverse(); 322 | if (settings.shuffleList) { 323 | videos = function (){for(var j, x, i = videos.length; i; j = Math.floor(Math.random() * i), x = videos[--i], videos[i] = videos[j], videos[j] = x);return videos;}(); 324 | } 325 | 326 | list += ''; 332 | 333 | list += '
'; 380 | settings.element.innerHTML = '
'+list+'
'; 381 | if(settings.element.getElementsByClassName('ytv-active').length===0){ 382 | settings.element.getElementsByTagName('li')[0].className = "ytv-active"; 383 | } 384 | var active = settings.element.getElementsByClassName('ytv-active')[0]; 385 | active.parentNode.parentNode.scrollTop = active.offsetTop; 386 | action.logic.loadVideo(first, settings.autoplay); 387 | 388 | if (settings.playlist){ 389 | utils.ajax.get( utils.endpoints.playlistInfo(settings.playlist), prepare.playlists ); 390 | } else if(settings.browsePlaylists){ 391 | utils.ajax.get( utils.endpoints.userPlaylists(), prepare.playlists ); 392 | } 393 | 394 | }); 395 | } else console.log ('Youtube-TV Error: Empty video list'); 396 | } 397 | }, 398 | action = { 399 | 400 | logic: { 401 | 402 | playerStateChange: function(d){ 403 | console.log(d); 404 | }, 405 | 406 | loadVideo: function(slug, autoplay){ 407 | var house = settings.element.getElementsByClassName('ytv-video')[0]; 408 | var counter = settings.element.getElementsByClassName('ytv-video-playerContainer').length; 409 | house.innerHTML = '
'; 410 | 411 | cache.player = new YT.Player('ytv-video-player'+id+counter, { 412 | videoId: slug, 413 | events: { 414 | onReady: settings.events.videoReady, 415 | onStateChange: function(e){ 416 | if( (e.target.getPlayerState()===0) && settings.chainVideos ){ 417 | var ns = settings.element.getElementsByClassName('ytv-active')[0].nextSibling, 418 | link = ns.children[0]; 419 | link.click(); 420 | } 421 | settings.events.stateChange.call(this, e); 422 | } 423 | }, 424 | playerVars: { 425 | enablejsapi: 1, 426 | origin: doc.domain, 427 | controls: settings.controls ? 1 : 0, 428 | rel: 0, 429 | showinfo: 0, 430 | iv_load_policy: settings.annotations ? '' : 3, 431 | autoplay: autoplay ? 1 : 0, 432 | theme: settings.playerTheme, 433 | wmode: settings.wmode 434 | } 435 | }); 436 | } 437 | }, 438 | 439 | endpoints: { 440 | videoClick: function(e){ 441 | var target = utils.parentUntil(e.target ? e.target : e.srcElement, 'data-ytv'); 442 | if(target){ 443 | if(target.getAttribute('data-ytv')){ 444 | // Load Video 445 | utils.events.prevent(e); 446 | var activeEls = settings.element.getElementsByClassName('ytv-active'), 447 | i; 448 | for(i=0; i 1 ? '.' + x[1] : '', 101 | rgx = /(\d+)(\d{3})/; 102 | while (rgx.test(x1)) { 103 | x1 = x1.replace(rgx, '$1' + ',' + '$2'); 104 | } 105 | return x1 + x2; 106 | }, 107 | parentUntil: function(el, attr) { 108 | while (el.parentNode) { 109 | if (el.getAttribute && el.getAttribute(attr)){ 110 | return el; 111 | } 112 | el = el.parentNode; 113 | } 114 | return null; 115 | }, 116 | ajax: { 117 | get: function(url, fn){ 118 | if (cache.exist(url)) { 119 | fn.call(this, JSON.parse(cache.get(url))); 120 | } else { 121 | var handle; 122 | if (win.XDomainRequest && !(navigator.appVersion.indexOf("MSIE 8")==-1 || navigator.appVersion.indexOf("MSIE 9")==-1)) { // CORS for IE8,9 123 | handle = new XDomainRequest(); 124 | handle.onload = function(){ 125 | cache.set(url, handle.responseText); 126 | fn.call(this, JSON.parse(handle.responseText)); 127 | if (Object.prototype.hasOwnProperty.call(JSON.parse(handle.responseText), 'error')){ 128 | cache.remove(url); 129 | var e = JSON.parse(handle.responseText); 130 | console.log('Youtube-TV Error: Youtube API Response: '+e.error.errors[0].reason+'\n'+ 'Details: '+e.error.errors[0].message); 131 | } 132 | }; 133 | } else if (win.XMLHttpRequest){ // Modern Browsers 134 | handle = new XMLHttpRequest(); 135 | } 136 | handle.onreadystatechange = function(){ 137 | if (handle.readyState === 4 && handle.status === 200){ 138 | cache.set(url, handle.responseText); 139 | fn.call(this, JSON.parse(handle.responseText)); 140 | } else if (handle.readyState === 4){ 141 | var e = JSON.parse(handle.responseText); 142 | console.log('Youtube-TV Error: Youtube API Response: '+e.error.errors[0].reason+'\n'+ 'Details: '+e.error.errors[0].message); 143 | } 144 | }; 145 | handle.open("GET",url,true); 146 | handle.send(); 147 | } 148 | } 149 | }, 150 | endpoints: { 151 | base: 'https://www.googleapis.com/youtube/v3/', 152 | userInfo: function(){ 153 | return utils.endpoints.base+'channels?'+settings.cid+'&key='+apiKey+'&part=snippet,contentDetails,statistics'; 154 | }, 155 | playlistInfo: function(pid){ 156 | return utils.endpoints.base+'playlists?id='+pid+'&key='+apiKey+'&maxResults=50&part=snippet'; 157 | }, 158 | userPlaylists: function(){ 159 | return utils.endpoints.base+'playlists?channelId='+settings.channelId+'&key='+apiKey+'&maxResults=50&part=snippet'; 160 | }, 161 | playlistVids: function(){ 162 | return utils.endpoints.base+'playlistItems?playlistId='+settings.pid+'&key='+apiKey+'&maxResults=50&part=contentDetails'; 163 | }, 164 | videoInfo: function(){ 165 | return utils.endpoints.base+'videos?id='+settings.videoString+'&key='+apiKey+'&maxResults=50&part=snippet,contentDetails,status,statistics'; 166 | } 167 | }, 168 | deepExtend: function(destination, source) { 169 | var property; 170 | for (property in source) { 171 | if (source[property] && source[property].constructor && source[property].constructor === Object) { 172 | destination[property] = destination[property] || {}; 173 | utils.deepExtend(destination[property], source[property]); 174 | } else { 175 | destination[property] = source[property]; 176 | } 177 | } 178 | return destination; 179 | } 180 | }, 181 | prepare = { 182 | youtube: function(){ 183 | if(typeof YT=='undefined'){ 184 | var tag = doc.createElement('script'); 185 | tag.src = "https://www.youtube.com/iframe_api"; 186 | var firstScriptTag = doc.getElementsByTagName('script')[0]; 187 | firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); 188 | } 189 | }, 190 | build: function(){ 191 | if (settings.channelId){ 192 | settings.cid = 'id='+settings.channelId; 193 | } else if(settings.user){ 194 | settings.cid = 'forUsername='+settings.user; 195 | } 196 | settings.element.className = "ytv-canvas"; 197 | if(settings.fullscreen){ 198 | settings.element.className += " ytv-full"; 199 | } 200 | utils.addCSS( '#'+id+' .ytv-list .ytv-active a{border-left-color: '+(settings.accent)+';}' ); 201 | // Responsive CSS 202 | if(settings.responsive){ 203 | utils.addCSS('#'+id+' .ytv-video{' 204 | +'position: relative; padding-bottom: 39.4%; /* 16:9 of 70%*/' 205 | +'height: 0; width: 70%;' 206 | +'} #'+id+' .ytv-video iframe{' 207 | +'position: absolute; top: 0; left: 0;' 208 | +'} #'+id+' .ytv-list{' 209 | +'width: 30%;' 210 | +'} #'+id+' .ytv-playlist-open .ytv-arrow{' 211 | +'top: 0px;}' 212 | +'@media only screen and (max-width:992px) {' 213 | +'#'+id+' .ytv-list{' 214 | +'position: relative; display: block;' 215 | +'width: 0; padding-bottom: 40%;' 216 | +'left: auto; right: auto;' 217 | +'top: auto; width: 100%;' 218 | +'} #'+id+' .ytv-video{' 219 | +'position: relative; padding-bottom: 56.25%; /* 16:9 */' 220 | +'height: 0; position: relative;' 221 | +'display: block; left: auto;' 222 | +'right: auto; top: auto; width: 100%;' 223 | +'}}' 224 | ); 225 | } 226 | // Temp Scroll Bar fix 227 | if (settings.listTheme == 'dark'){ 228 | utils.addCSS( ' #'+id+'.ytv-canvas ::-webkit-scrollbar{border-left: 1px solid #000;}' 229 | + ' #'+id+'.ytv-canvas ::-webkit-scrollbar-thumb{background: rgba(255,255,255,0.2);}'); 230 | } 231 | // Optional Light List Theme 232 | if(settings.listTheme == 'light'){ 233 | utils.addCSS( ' #'+id+'.ytv-canvas{background: #ccc;}' 234 | + ' #'+id+'.ytv-canvas ::-webkit-scrollbar{border-left: 1px solid rgba(28,28,28,0.1);}' 235 | + ' #'+id+'.ytv-canvas ::-webkit-scrollbar-thumb{background: rgba(28,28,28,0.3);}' 236 | + ' #'+id+' .ytv-list .ytv-active a{background: rgba(0,0,0,0.2);}' 237 | + ' #'+id+' .ytv-list a{color: #282828; border-top: 1px solid rgba(0,0,0,0.1); border-bottom: 1px solid rgba(204,204,204,0.5);}' 238 | + ' #'+id+' .ytv-list a:hover, #'+id+' .ytv-list-header .ytv-playlists a:hover{ background: rgba(0,0,0,0.2);}' 239 | + ' #'+id+' .ytv-list a:active, #'+id+' .ytv-list-header .ytv-playlists a:active{ background: rgba(0,0,0,0.2);}' 240 | + ' #'+id+' .ytv-list .ytv-thumb-stroke{outline: 1px solid rgba(0,0,0,0.1);}' 241 | + ' #'+id+' .ytv-list .ytv-thumb{outline: 1px solid rgba(255,255,255,0.5);}' 242 | + ' #'+id+' .ytv-list-header{-webkit-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.2); -moz-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.2); box-shadow: 0 1px 2px rgba(255, 255, 255, 0.2);}' 243 | + ' #'+id+' .ytv-list-header a{background: rgba(0,0,0,0.2);}' 244 | + ' #'+id+' .ytv-playlists{background: #ccc;}' 245 | ); 246 | } 247 | }, 248 | userUploads: function(userInfo){ 249 | if (userInfo && userInfo.items.length > 0){ 250 | settings.pid = userInfo.items[0].contentDetails.relatedPlaylists.uploads; 251 | utils.ajax.get( utils.endpoints.playlistVids(), prepare.compileVideos ); 252 | } else console.log ('Youtube-TV Error: API returned no matches for: '+(settings.channelId ? settings.channelId : settings.user)+'\nPlease ensure it was entered correctly and in the appropriate field shown below. \nuser: \'username\' or channelId: \'UCxxxx...\''); 253 | }, 254 | selectedPlaylist: function(playlistInfo){ 255 | if (playlistInfo && playlistInfo.items.length > 0) { 256 | if (!settings.channelId && !settings.user){ 257 | settings.cid = ('id='+(settings.channelId = playlistInfo.items[0].snippet.channelId)); 258 | } 259 | settings.currentPlaylist = playlistInfo.items[0].snippet.title; 260 | settings.pid = playlistInfo.items[0].id; 261 | utils.ajax.get( utils.endpoints.playlistVids(), prepare.compileVideos ); 262 | } else console.log ('Youtube-TV Error: API returned no matches for playlist(s): '+settings.playlist); 263 | }, 264 | compileVideos: function(res){ 265 | if (res && res.items.length > 0){ 266 | var playlists = res.items, 267 | i; 268 | settings.videoString = ''; 269 | for(i=0; i 0){ 278 | var list = '
    ', 279 | playlists = res.items, 280 | i; 281 | for(i=0; i'; 288 | list += '
    '; 289 | list += ''+(data.title)+''; 290 | list += ''; 291 | } 292 | list += '
'; 293 | 294 | var lh = settings.element.getElementsByClassName('ytv-list-header')[0], 295 | headerLink = lh.children[0]; 296 | headerLink.href="#"; 297 | headerLink.target=""; 298 | headerLink.setAttribute('data-ytv-playlist-toggle', 'true'); 299 | settings.element.getElementsByClassName('ytv-list-header')[0].innerHTML += list; 300 | lh.className += ' ytv-has-playlists'; 301 | } else console.log ('Youtube-TV Error: Returned no playlists'); 302 | }, 303 | compileList: function(data){ 304 | if(data && data.items.length > 0){ 305 | utils.ajax.get( utils.endpoints.userInfo(), function(userInfo){ 306 | var list = '', 307 | user = { 308 | title: userInfo.items[0].snippet.title, 309 | url: '//youtube.com/channel/'+userInfo.items[0].id, 310 | thumb: userInfo.items[0].snippet.thumbnails['default'].url, 311 | summary: userInfo.items[0].snippet.description, 312 | subscribers: userInfo.items[0].statistics.subscriberCount, 313 | views: userInfo.items[0].statistics.viewCount 314 | }, 315 | videos = data.items, 316 | first = true, 317 | i; 318 | settings.channelId = userInfo.items[0].id; 319 | if(settings.currentPlaylist) user.title += ' · '+(settings.currentPlaylist); 320 | if (settings.sortList) videos.sort(function(a,b){if(a.snippet.publishedAt > b.snippet.publishedAt) return -1;if(a.snippet.publishedAt < b.snippet.publishedAt) return 1;return 0;}); 321 | if (settings.reverseList) videos.reverse(); 322 | if (settings.shuffleList) { 323 | videos = function (){for(var j, x, i = videos.length; i; j = Math.floor(Math.random() * i), x = videos[--i], videos[i] = videos[j], videos[j] = x);return videos;}(); 324 | } 325 | 326 | list += ''; 332 | 333 | list += '
'; 380 | settings.element.innerHTML = '
'+list+'
'; 381 | if(settings.element.getElementsByClassName('ytv-active').length===0){ 382 | settings.element.getElementsByTagName('li')[0].className = "ytv-active"; 383 | } 384 | var active = settings.element.getElementsByClassName('ytv-active')[0]; 385 | active.parentNode.parentNode.scrollTop = active.offsetTop; 386 | action.logic.loadVideo(first, settings.autoplay); 387 | 388 | if (settings.playlist){ 389 | utils.ajax.get( utils.endpoints.playlistInfo(settings.playlist), prepare.playlists ); 390 | } else if(settings.browsePlaylists){ 391 | utils.ajax.get( utils.endpoints.userPlaylists(), prepare.playlists ); 392 | } 393 | 394 | }); 395 | } else console.log ('Youtube-TV Error: Empty video list'); 396 | } 397 | }, 398 | action = { 399 | 400 | logic: { 401 | 402 | playerStateChange: function(d){ 403 | console.log(d); 404 | }, 405 | 406 | loadVideo: function(slug, autoplay){ 407 | var house = settings.element.getElementsByClassName('ytv-video')[0]; 408 | var counter = settings.element.getElementsByClassName('ytv-video-playerContainer').length; 409 | house.innerHTML = '
'; 410 | 411 | cache.player = new YT.Player('ytv-video-player'+id+counter, { 412 | videoId: slug, 413 | events: { 414 | onReady: settings.events.videoReady, 415 | onStateChange: function(e){ 416 | if( (e.target.getPlayerState()===0) && settings.chainVideos ){ 417 | var ns = settings.element.getElementsByClassName('ytv-active')[0].nextSibling, 418 | link = ns.children[0]; 419 | link.click(); 420 | } 421 | settings.events.stateChange.call(this, e); 422 | } 423 | }, 424 | playerVars: { 425 | enablejsapi: 1, 426 | origin: doc.domain, 427 | controls: settings.controls ? 1 : 0, 428 | rel: 0, 429 | showinfo: 0, 430 | iv_load_policy: settings.annotations ? '' : 3, 431 | autoplay: autoplay ? 1 : 0, 432 | theme: settings.playerTheme, 433 | wmode: settings.wmode 434 | } 435 | }); 436 | } 437 | }, 438 | 439 | endpoints: { 440 | videoClick: function(e){ 441 | var target = utils.parentUntil(e.target ? e.target : e.srcElement, 'data-ytv'); 442 | if(target){ 443 | if(target.getAttribute('data-ytv')){ 444 | // Load Video 445 | utils.events.prevent(e); 446 | var activeEls = settings.element.getElementsByClassName('ytv-active'), 447 | i; 448 | for(i=0; i