├── .gitignore ├── README.md ├── bower.json ├── demos ├── angular.html ├── angular.min.js ├── audio-angular.html ├── audio.html ├── audio2.html ├── basic.html ├── bpm.html ├── cc_audio │ ├── encom_part_ii.json │ ├── encom_part_ii.mp3 │ └── encom_part_ii.ogg ├── exploded.html ├── foundation.min.css ├── full-screen.html ├── index.html ├── n-times.html ├── ping-pong.html ├── speed.html ├── stoppable.html ├── synched_playback.js └── visual_test.html ├── dist ├── layout.html ├── platform.js ├── polymer.html ├── polymer.js ├── polymer.js.map ├── x-gif.angular.js ├── x-gif.css ├── x-gif.html ├── x-gif.js └── x-gif.local.html ├── gulpfile.js ├── index.html ├── package.json └── src ├── exploder.js ├── gif.js ├── playback.js ├── strategies.js ├── stream_reader.js ├── utils.js ├── x-gif.angular.js ├── x-gif.html ├── x-gif.js ├── x-gif.polymer.js └── x-gif.scss /.gitignore: -------------------------------------------------------------------------------- 1 | bower_components 2 | node_modules 3 | demos/audio 4 | dist/images 5 | /*.gif 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # <x-gif> 2 | 3 | _The GIF tag the internet deserves_ 4 | 5 | ## Usage 6 | 7 | **<x-gif>** is a web component for flexible GIF playback. Speed them up, slow them down, play them in reverse, synch multiple beats to a rhythm, synch them to audio, whatever you like. 8 | 9 | ```html 10 | 11 | ``` 12 | 13 | ###Playback modes: 14 | 15 | Mutually exclusive. Can't be changed once initialised (create a new x-gif if you want a new mode) 16 | 17 | `speed="1.0"` (default mode) 18 | Plays back the GIF at its natural framerate multiplied by the value of the attribute. Can be updated and will have immediate effect. 19 | 20 | `sync` 21 | Defers playback to an external object. The DOM element will then expose a `clock` function to facilitate playback. Cannot be changed. 22 | 23 | `bpm="120"` 24 | Syncs GIFs to a given beats-per-minute. If multiple x-gifs are on the page, they will all be synced together. By default, will spread long GIFs over multiple beats, unless the `snap` option is also included. Uses `sync` and `clock` under the hood. Can be changed and will take immediate effect. 25 | 26 | ###Options: 27 | 28 | `stopped` 29 | Regardless of playback mode, this will prevent the GIF from animating. Removing this attribute resumes playback. In `speed` mode, the GIF will always resume playback from the beginning. 30 | 31 | `fill` 32 | Causes the GIF to expand to cover its container, like if you had used `background-size: cover; background-position: 50% 50%` with a normal GIF. Without `fill`, an x-gif behaves like an inline-block element, just like a normal tag. 33 | 34 | `n-times="3.0"` (speed mode only) 35 | Stops playback (by adding the attribute `stopped`) after a set number of times. Can be fractional e.g. `0.9` will play the first 90% of the GIF then stop. Removing the `stopped` attribute will restart the playback. 36 | 37 | `snap` (sync & bpm modes only) 38 | Instead of allowing longer GIFs to sync to multiple beats, force them to fit into only one. 39 | 40 | `ping-pong` 41 | Boolean attribute. Plays the GIF front-to-back then back-to-front, which looks more awesome for some GIFs. Works with all playback modes. Can be removed/added at any time. 42 | 43 | ###Debugging: 44 | 45 | `debug` 46 | Turns on debug output from the Gif Exploder, which can help track down errors with some GIFs being parsed incorrectly. 47 | 48 | `exploded` 49 | For visual inspection of the frames. Stops playback, and renders each frame out side-by-side. Many frames will appear semi-transparent, because that's how GIFs work. But this might come in handy. 50 | 51 | ##What does it do? 52 | 53 | * AJAX fetches the GIF as a binary stream 54 | * Slices the GIF into frames like a total boss 55 | * Stacks the frames one on top of the other 56 | * Starts a `requestAnimationFrame` loop to play back the gif at its natural frame rate 57 | 58 | **[Here's a demo! It just might work in your browser!](http://geelen.github.io/x-gif)** 59 | 60 | **[Check out the rest of the demos](http://geelen.github.io/x-gif)** 61 | 62 | ## Usage 63 | 64 | ```html 65 | 75 | 76 | ``` 77 | 78 | This will detect support for Web Components, shim them if needed, then load x-gif with a HTML import. 79 | 80 | ## Roadmap 81 | 82 | * Web Component - YES! (zero-dependencies on Chrome 36!) 83 | * Polymer element - Nah, just use the Web Component 84 | * Angular directive - Nah, just use the Web Component 85 | * React component - Nah, just use the Web Component 86 | * Ember component - Nah, just use the Web Component 87 | 88 | ## Contributing 89 | 90 | 1. Fork it! 91 | 2. Create your feature branch: `git checkout -b my-new-feature` 92 | 3. Commit your changes: `git commit -m 'Add some feature'` 93 | 4. Push to the branch: `git push origin my-new-feature` 94 | 5. Submit a pull request :D 95 | 96 | ## License 97 | 98 | [MIT License](http://opensource.org/licenses/MIT) 99 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "x-gif", 3 | "authors": [ 4 | "Glen Maddern " 5 | ], 6 | "main": "dist/x-gif.html", 7 | "description": "The GIF element the internet deserves", 8 | "license": "MIT", 9 | "keywords": [ 10 | "web-components" 11 | ], 12 | "ignore": [ 13 | "**/.*", 14 | "node_modules", 15 | "bower_components", 16 | "test", 17 | "tests" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /demos/angular.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | <x-gif> 8 | 9 | 10 | 44 | 45 | 46 |
47 |
48 |
49 |

<x-gif>

50 |

The GIF tag the internet deserves

51 |
View source on GitHub
52 |
53 |
54 | 55 | 60 | 64 | 65 |

Normal playback

66 |

Something not look right? Post an issue on GitHub with the URL of the image

67 |
68 | 69 |
70 |
<x-gif src="{{ trustedUrl() }}"></x-gif>
71 |
72 |
73 | 74 |

Ping-pong

75 |
76 | 77 |
78 |
<x-gif src="{{ trustedUrl() }}" ping-pong></x-gif>
79 |
80 |
81 | 82 |

Speed

83 |
84 | 87 | 88 |
89 |
<x-gif src="{{ trustedUrl() }}" speed="{{ gif.speed }}"></x-gif>
90 |
91 |
92 | 93 |

Synced to Audio

94 |
95 |
96 | 100 |

Playback speed: {{ gif.playbackRate | number:1 }} + - 102 |

103 |
104 |
Loading beat data...
105 | 106 |
107 |
<x-gif src="{{ trustedUrl() }}" synced></x-gif>
108 |

109 | Audio: Encom Part II by Daft Punk. CC0 1.0 Universal. 110 |
111 | Source: 112 | Archive.org 113 |
114 | Beat metadata provided by Echo Nest 115 |

116 |
See a full screen demo
117 |
118 |
119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 |
130 | 131 |
132 |
Made with <3 by
133 | 134 |
135 | @glenmaddern 136 |
137 |
138 |
139 | 140 | 141 | 142 | 182 | 192 | 193 | 194 | -------------------------------------------------------------------------------- /demos/audio-angular.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Toggle Button 5 | 6 | 16 | 17 | 18 | 19 | 23 | 24 | 25 | 26 | 27 | 68 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /demos/audio.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Autoplay audio 5 | 15 | 16 | 25 | 26 | 27 | 28 | 32 | 33 | 34 | 70 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /demos/audio2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Toggle Button 5 | 6 | 7 | 16 | 17 | 18 | 19 | 23 | 24 | 25 | 62 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /demos/basic.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Toggle Button 5 | 6 | 7 | 10 | 11 | 12 |

IMG

13 | 14 | 15 |

X-GIF

16 | 17 | 18 | -------------------------------------------------------------------------------- /demos/bpm.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Toggle Button 5 | 6 | 7 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /demos/cc_audio/encom_part_ii.json: -------------------------------------------------------------------------------- 1 | {"response":{"status":{"version":"4.2","code":0,"message":"Success"},"track":{"status":"complete","title":"Encom Part II","artist":"Daft Punk","analyzer_version":"3.2.2","audio_summary":{"time_signature":4,"tempo":160.669,"energy":0.449880047474374,"liveness":0.10151058608195748,"analysis_url":"http://echonest-analysis.s3.amazonaws.com/TR/TRUAPOG144DC2C9758/3/full.json?AWSAccessKeyId=AKIAJRDFEY23UEVW42BQ&Expires=1395265690&Signature=PbppIfMhFkB/gqNkvg%2BaP5rFwY8%3D","speechiness":0.03791989038906429,"acousticness":0.46672014000836765,"danceability":0.41425091237010037,"key":7,"duration":137.67791,"loudness":-14.446,"valence":0.36040519924019443,"mode":0},"bitrate":96,"id":"TRUAPOG144DC2C9758","samplerate":44100,"md5":"71a1a0ceaeb0dda9ed78217c25a4bbe0","analysis":{"meta":{"analyzer_version":"3.2.2","platform":"Linux","detailed_status":"OK","filename":"filename.ogg","artist":"Daft Punk","album":"TRON: Legacy Soundtrack (Disc 2)","title":"Encom Part II","genre":"Electronic/Techno","bitrate":96,"sample_rate":44100,"seconds":137,"status_code":0,"timestamp":1395263316,"analysis_time":4.13},"track":{"num_samples":3035798,"duration":137.67791,"sample_md5":"66eac08016916a6dfbc446e8a7f4988e","decoder":"ffmpeg","decoder_version":"FFmpeg 0.6.5","offset_seconds":0,"window_seconds":0,"analysis_sample_rate":22050,"analysis_channels":1,"end_of_fade_in":0.38014,"start_of_fade_out":131.1463,"loudness":-14.446,"tempo":160.669,"tempo_confidence":0.08,"time_signature":4,"time_signature_confidence":0.991,"key":7,"key_confidence":0.545,"mode":0,"mode_confidence":0.434,"code_version":3.15,"echoprint_version":4.12,"synch_version":1,"rhythmstring":"eJxVW1liLCcMvMocAcQiuP_Folpov3y82J7pBbSUSiXSe4v1G_E7vznPr-Pf3b89f3303x6_Hb-cv7V-e-FT_DfrkvrRfj3qysOLI3_7_nbXnQ13xtq_HPXV-vVzfxm_pWvH_UWrD1v9uet7_VjtNzefeuumc36ZuCH4nv47_bcGXsB1Bd_dZ31Yq6n7Jp6QP3xwdEny4sD7Qq8NrL7XU2YtBWsI7KvPhr-2Hx5cwshf1seNWz7aw6qX9qavp14auiH4Jf6KW5_wSbCLXoglxN9WcWt92mnkWmWUeXtLLIBLqf2sif3UouAK_MDrYdR6xFi65Pwu3rVp1M0t02h4YfAL_JtJd9W9sey2-nLWnb3eeuknXT_x66EFo-FZcnRtr1_aBFvR67DM3jvNDysF7bXpPLqj_mVwqTYWzdB-p0x_GStYUcfOauO9n1-tHHfRxHjjoWfqznoeLqvLM_5MwbCkKSquNlf4rt-IlHpWHtzLELgvlvVrvKhUdMN-Q34ctJDtvv354BVH4ZLvMZuhgd0jYRLBipdMmR2Rr0CqrxisA9bEe-uDpehM5U_I7bs8REPVfhYD51RCIP6RKeN3dVPwwYifi1fXNacx03p2Or8-L08spmOFepQd-mIUlVGjXyVOJRRWUb6eckLt-0T9pWRb-HXAKzBIv0zrvZ0N9d5sjB6GWQW18ida2X_KPM_YTalHZ8DGDJ-FjeE7xNugw5Rj2CHQaCi95i9g00r-ylZESeBCumm8fKpvOzbc-MJAvFYolKUREJe2cqwQo-ItL4kU9W30pTCsnSKlsFCl69tEHCFN4Pu7vnBxvuN5weXc-VKRkLgdOUQdrnQbDrFSIlDCRwXBMHXdA1jEB8eP_Z5-pjIOz4WVhWjlu8llpm4rq6TzOWinxRiACxFJF2H7PIhEqceXSetrZCv3jvwbCNxZD6iAmd7-6M6HTZwo9xuC6_6e0xeVD84QBF0BDlK9fk_DsmG7nob7utAE1xBRLr3OfeJ-AMLw90A3PB6rksE68n4qQbGoCmU-TLF353N8pFfL0BiM9N6XICm_3eHm2oeCXvlXTwVgqRLiWlxzFHF6DWLkMrJfrG8miwvgi_5_Nxjn38SAKeMzwkj6Dkkwv8VxnSrKytyjbff1F33ju8q7wafAIGJGpRfr1lbKGgP_HtTk3qv6N_8eFPPvamRS_-fm_ffzxcd8_CBUaAh5qOrEItTd7UpwXT68w_NXJq_iOn_-TlV-C-6IbPtzXSGbYYRLES3QW7mTxTKcfnLyPU2WT5rdtX_C8S6WcHilw-X77dCKgMngYAgzOmCOekJ2Fq8OPtHIa7TDpUierrbdiCxLfWWlcquyFQG0WK0Ws4fr1uKWSh3zhLkc28xsK1AKjYAHjYsLlAjA-aShO17dphZYzywEWZ3gXukAOGl8FiKiCQpQe1gWBJZAI5a3CKL0esYlP7ve2LEFgBgogumilS8x6wV3v_Ja752KM9xfH9QPrmASf_AhkBios8RlyjvYI0yRgqtOhjKRhkxlQRYDqQswgEGHNZfhXDu6JLEsm4nH3meXsinzoFbYRGgUT8zfG-JYhVbwTcfaz_wIZiPmjiGkqzcFLitjppYFuGDNJZfgjuFGcKtNfhXw4xI75tbDBZGbWu0rrSbIHX9UNLIsTO7DRGbJ3WBJDAamS2fN7K4xPVkVL4D1IHjxHoQ2jH0Fksmqj9o-VIsbL0nh0nQgiUM_RIC54DMUnuKU5wF6FyNLGq7jSYspuBTCsA3xLwp_pqtbVb-pxfPP8k6FDslqIrzwEATWVA-xFUJ03nSwo7hy0-IHTdiG-0Fq3T0kYRsoA-fjV4ZJraXQpnZPljRJiBB_m1iKrYDctKFCU28DYUiWv0lPAxiaqii2eFlO8HXUsibp3xKswCT1F9DmCIHxShrsygydVl9qW3oSJcmymRbYdibToGP9DeWNqwkEXmMRhqkQbUCpoQyBaw_p6VWvFDD4fWCeZN4wb8FEmRQ4fBUCzr_gzYjfpqaBTwbNCL5oMj0CLtn51ZSOO2opoBkIkDFdzck8Fnm0OgxE0VEIdgW-maOIshAZIMt-QCm-XIe3_L9VNTazRYRyK2cOWTk7ry8l1VXARJcXqYXRbY_8ifaw5HB79eiDKCAgMJL4LrZlbGLX5-s49IZ4oCANuZPshVJvHFrVH3lR88koK2-aaw1BgQu4YD5ZKlKc_dUYh_Ng036PmYLawqUv1bTRgM2xIbwGKsHN3R0hC0-y1BJS4tuu8gxN-X7V9s-tCiCVkfWgWQ02ychQk3QV1CE639HJLPXegKhDiEdyXPm7YSVT-0X3SoAc3AOsPUWyxnjkO8Jk-bjIok2KdtwdsvvdDAcW8E4YZL93mYfsay8ctXTZZsVfQthQhWHkmJIQq5h8rHlg45ctg2jCIr0Asm117vgQ-Xy5y2Rjv1XGQhAVzAiC52aLxoRkX8l3uhcMQlMFbQX0fI9BO9mP3nnCDdytTUfuZ8Z2H-e6lCsQ0mBAh8kCW-EJgC8CwFRTNW13mfGojyWCkXo65FB2JSil2v4KKiINm5Zko4sFBOKOng4CWEUOJJEUx3WVKQA_S9zwPMGBKorYIqoua009q6IIlt_Ul6BDXIYrfIgnsaNB2WEtBLZVSMKh9W8gFiru4DAxQ8IcnJFq9NURgU1LJvFCl5wZiAbgfT3natVruKFx3Fshq3fCcbBd1wOphkQzSVy0JfIZeN6FUFauGmMMt1_GKnc1YZ-p6tzaky1YE6FiHbb2R2KLKTS6nkrXKzZhEx16R9um207jA6_8X7a-7ltVduCLijA4rsmyqJxbLKm-n9A1GtPnMDNT0b3EAfPTr0JwRkBBnqYQwyLj_IP7JtIp5B3tqZP369pD8a4uaqpZzqdogmsnw5hCkuUAcZLZ3DPTeoyhrSq1VfoAAG60ulijqBTJ-PhHAA0G-DqvqpG74E-VEG3jdf_jfI1Ke-8Eel_ZRT3Vdmv2FK7lujj_gkXLHCyo9u_uunN8jeemJpeWIsRGkpGmOikd8f4egR-vqXq94XITxraeZJ82dsPLG9f9ujEvW0tZWsrrz_q37CQt2I8HkOeEeddW9_9i1thzzNuI-ykvglJuUkLJDans-OMa-Imu-LqyU1ealjUlr1nMbq7fT49Re5tOH6m-S1hP31OrSqovx9VpaMF3v01QZaVFsQZFwYyvFdOiyhpnu8QsxVI9AWAG8AD2SaNenyS1LagGgy_3Q4S3X8u4So3-EmQoheR5CQ9EOBYoBV5nsBQZ3eToAOPVrGwCboLwDs6H_J5XACnIrCISh7ukRix1kiC9rE2rDzUdW3zcUavL6ONnINBLbmhsE90IHa2MVlGvUoaBH1MiGpVu-pPF5NWXYFID-JH-iDMXiHRpAICl5DLYp5FxAYCRwpO31ItGM6-m84UXh5MOJPSUCruNoYC9o3BVM1seZz-rjk_9EFvfxndvKai1pdHz03RrM2x-qRkdaeRY9FA5wjut-6ymrjmoNi7eA2d0llu0I5CLxzBXinqNtAYIyZc15HKQcchfzGdhkZVv_IBfYVjQJYQAmNUgch31R9lJPsIzFkQoHD0V8E9DT-vXcADbWuNv0BX5E4YTdpu5pMCU8GENejRPCDY7N89sqHyhkC_mjuAdQbwEoWnbEAVTPPeKU_HhTqSupm-bO2ketWR3NyCjf62TR2Q0hPiRdYKvndGV8S-eD-LAztdjXBOf8zoXblOCNkn-eh3CprH88PDqtwSO-ZQpUV-KX_l-035yCqa_aco6_7RX_C2-Vso72K9-D3UOyVGGzA4SYLFrSYV2_5Dkm8yiqQub_Kpi6Tql_mhojQyZ7fX0J5c9lZNwm5Jy8LmyhpGgshUsjfkNrZYR2ShpYalvC0F3uAsjDHYrimyjkm1S_xhgE7mREDC6ZdDtEdKmJBHBZE7h3GoKRddmwq0yKfdnYe5hHqMUZS2DfoQbfg-QVM9IC9Kqt1Z9Hcn8vH9fh2t_ZWj7NyrZRmR_1wSHAqlGwT2uBjLhgdZG079pFrDRIyJ0WEEJdBgzHJYCASoaD0kJcEnXaKVrMAjbSJ8I8kxVK-z3HBc-KmUs7sB-eqNN-ZlhjuoSZZ2wArYYzt8oJ5agfrHf3CEQDpWgJpVOtUo5gBa3MxjUxk-OQinHds33CErMRGDdFsYeiesKZQgxh9MqKlIeiBB4mCeuE_CzGClmW7DdJq3GVrtCHEnZJOOc9fgxtnZJxwOl7lJEQtQiFlluvuETeoBNBEG1Xarxr9WFSNytll0pAkk1ZBoOzFNQcODRzbImXWtIJT36dlIaoAaFTjuhT0-V666aZbMdaYaMZRQOC-9zPGq0lChNxw8o_Qk5gqcA-ITDSFJr6-5mWfhbnB8uCeNLmtSWfgh_oUvZgsn9_9yx8zyoErtY5Fip5puFaj9Et8Cz2mvRfZc6ECoblNDdtAgmh2e9YpcCIYL87m8fZEHsfd70TijqaYfWEKZ8W4rofFOv7SbFHPqpa_GGX6JSyb9f_sQrqTtf_gvOKEXtv_vvfCxeKsE2dq8PnRjX-Y1--jvqARK25htw-CDBcBEzIc4Pal0aQnB0ji95gwKd0BBxSDfTZr_SmDfVGvWP4PT9SWp5zOzpkJcDfXyO2FsV_RpIWcdTT530fLh1e_oP7WveKaDM7tnStpSlvnUwy1GKWBgIr1PfSCJs_4TiEiyv85rHY6SSRooClhrLJZvqfIdMgATZXiHRTFHiEllQ6khG9FcQNXdTX_dXEMLRSm0qP7Kc6j9Edtb6tMrny0sdB1JHQcUoOOSvOvYzlOH1X6DQVAcOWAPkQHFSGQVsw_JNdPgKQeUZDkoGxerz1fyJkX1H2QYzVUY3hSyIJxQlMRUEprAiPblkGdHQuR42vYsJRqwurMsTANgp-8fpHt76YEvzCYouMIDagzaCmsuRrFQxQwo9Na_vqtsSZsxWsNLBUz7QHHVwaXpUSYXpkIEuPuWMBxGflr9SyMyWF3gbPCIx5H2qeABOOPyq29DzcdJjUn--rBOpwtNkw-ORU9AQ11ur0LvS3mke1Zfm0WdowqF57uaFHTnDICXgqgkG38hvXHhkvG4ZHRs4j8qIq-DKdNkTzUgeGpiChNCidEgJIB_T3SlSqvx3pUTuJmYv5eI0Sxk-vCAudTx8Hiz_2-LvcIh_Jww8whQoI31dazi5zXfQxCNuC0s-DgFkl1KuWdun029P6ZnlXe2J7rYao-w3N94fcph_L-bk9PDDWtOyhqM4H0QXjYzW-WQBz9yHVnWsr3-nZNaT5Nxxs3k5lkc0MF8m7yq8N165BTlr8UzzDm2s_c63STnr3wBCGmC-pm1Igf1nRD5_78yINJ0tFXrsrxWbT7OQqrJ1MGTtN21j55nHJ6t0NCiVlFZd1Ltr-P-OCLXn8dScZ6p71amoIRqg81EUPp4StamjpkccwwDMDLPczyNb9-hMj4d8UoqfdNh-Es_RqOuP7aMY7wwGlfn-pKzXBb7WbD1_6sTTkkaA_NzqfOZScVmemAfX5JGZQ_07q-Gpf7CUv17O8uN9z0cpCB7DhHk6syquuMXUQQ0dOKiH-rgmmh54ZulwG-gj60U3DW4qVZGLFYLgFCq9R5OdSSy6tBDx2vjcfT6mzHel9qz4JvebT_OvGIeom58aGEzOC8ShDt9_dAREA3ZSbvzFxenB-F3DFvHiUF22x-niWtQVVecIilpCV0lNA2f71F6d0_HBRxSKPt_U6Eqhw1Tq6qlv5Nx-PgPJ12k-dXikpe_3qefwc7_TKoS9a4gfrlae6XeNnkKz9ENLtfNJca5XcMqWbgfhSKck-YxOXwLofZolRKo4ZtfII67Y_vWpKU5Gu04pofR-xzvu6yCv_E6RSSVkawjFsFEdn-ONJlFJt5pQC42Miu6RGwVGngpLDw8456Yg5yM0RzOzr7Th-NlHq6cnyKqEqTGhTh2509jbIChpbK13fnN81eM9yadrv_OG3-Gq63tyqrrF_041PSlb469c6uJMru-y1Oz6oyG3UPAxgTfibrCJh9ixPh4ZnwBzvD4zRp8kFigstmk67szdx_8Lw9M8dESMVcDHeGlKpaiHE_NrTCz-jdev1KVsylWG32zFcxPB2J8iZ515H9NWtjLWGGWA8I7e-DtYgN7053KKIpV3ypPdZnAg7_6F0ngnLJrn5fY_9QOdi5HevH_vdB4LX1gCc8RcT0x9hMcnz_89JI9zue11iqA9SA0JhILW9AA27mMIZM3rTTZY4OUhMRIHvJg6kuHO3z9H8XwEVqck9L8CyNrqhljhLMmhOhrARz4tDlGGZYuEQiKou_Hv7zjBkTtMIAGR8R_ol8a7","rhythm_version":1},"bars":[{"start":0.99521,"duration":1.51353,"confidence":0.42},{"start":2.50874,"duration":1.4873,"confidence":0.65},{"start":3.99605,"duration":1.4973,"confidence":0.567},{"start":5.49335,"duration":1.49989,"confidence":0.787},{"start":6.99324,"duration":1.49938,"confidence":0.625},{"start":8.49262,"duration":1.49287,"confidence":0.604},{"start":9.98549,"duration":1.4914,"confidence":0.508},{"start":11.47688,"duration":1.49667,"confidence":0.392},{"start":12.97356,"duration":1.4956,"confidence":0.345},{"start":14.46915,"duration":1.49149,"confidence":0.585},{"start":15.96064,"duration":1.50109,"confidence":0.707},{"start":17.46173,"duration":1.4903,"confidence":0.201},{"start":18.95203,"duration":1.49292,"confidence":0.482},{"start":20.44495,"duration":1.48989,"confidence":0.678},{"start":21.93484,"duration":1.49122,"confidence":0.778},{"start":23.42606,"duration":1.49757,"confidence":0.9},{"start":24.92363,"duration":1.49484,"confidence":0.701},{"start":26.41847,"duration":1.49604,"confidence":0.836},{"start":27.91451,"duration":1.49536,"confidence":0.39},{"start":29.40987,"duration":1.49717,"confidence":0.728},{"start":30.90704,"duration":1.49227,"confidence":0.584},{"start":32.39931,"duration":1.48905,"confidence":0.616},{"start":33.88835,"duration":1.49354,"confidence":0.457},{"start":35.38189,"duration":1.49479,"confidence":0.451},{"start":36.87668,"duration":1.48806,"confidence":0.257},{"start":38.36475,"duration":1.49969,"confidence":0.596},{"start":39.86444,"duration":1.49799,"confidence":0.713},{"start":41.36242,"duration":1.48794,"confidence":0.762},{"start":42.85036,"duration":1.50229,"confidence":0.609},{"start":44.35265,"duration":1.4919,"confidence":0.657},{"start":45.84455,"duration":1.49016,"confidence":0.468},{"start":47.33471,"duration":1.50213,"confidence":0.689},{"start":48.83684,"duration":1.49577,"confidence":0.3},{"start":50.33261,"duration":1.48807,"confidence":0.625},{"start":51.82068,"duration":1.50276,"confidence":0.437},{"start":53.32344,"duration":1.50141,"confidence":0.205},{"start":54.82485,"duration":1.48268,"confidence":0.458},{"start":56.30753,"duration":1.48595,"confidence":0.5},{"start":57.79348,"duration":1.49654,"confidence":0.463},{"start":59.29001,"duration":1.34792,"confidence":0.558},{"start":60.63794,"duration":1.4583,"confidence":0.641},{"start":62.09624,"duration":1.49587,"confidence":0.283},{"start":63.59211,"duration":1.49555,"confidence":0.8},{"start":65.08766,"duration":1.48991,"confidence":0.317},{"start":66.57756,"duration":1.48995,"confidence":0.762},{"start":68.06751,"duration":1.49765,"confidence":0.505},{"start":69.56517,"duration":1.49963,"confidence":0.23},{"start":71.0648,"duration":1.49712,"confidence":0.274},{"start":72.56191,"duration":1.49717,"confidence":0.509},{"start":74.05908,"duration":1.48979,"confidence":0.412},{"start":75.54888,"duration":1.48743,"confidence":0.438},{"start":77.0363,"duration":1.49974,"confidence":0.397},{"start":78.53604,"duration":1.4997,"confidence":0.734},{"start":80.03574,"duration":1.49838,"confidence":0.271},{"start":81.53412,"duration":1.49477,"confidence":0.588},{"start":83.02889,"duration":1.49617,"confidence":0.639},{"start":84.52506,"duration":1.4975,"confidence":0.413},{"start":86.02257,"duration":1.49514,"confidence":0.69},{"start":87.51771,"duration":1.4901,"confidence":0.855},{"start":89.00781,"duration":1.49307,"confidence":0.378},{"start":90.50088,"duration":1.49558,"confidence":0.268},{"start":91.99646,"duration":1.49357,"confidence":0.434},{"start":93.49003,"duration":1.49658,"confidence":0.58},{"start":94.98661,"duration":1.49528,"confidence":0.737},{"start":96.48189,"duration":1.48781,"confidence":0.771},{"start":97.9697,"duration":1.48845,"confidence":0.371},{"start":99.45815,"duration":1.49847,"confidence":0.617},{"start":100.95663,"duration":1.50343,"confidence":0.779},{"start":102.46006,"duration":1.49559,"confidence":0.657},{"start":103.95565,"duration":1.49308,"confidence":0.387},{"start":105.44873,"duration":1.48928,"confidence":0.442},{"start":106.93802,"duration":1.49799,"confidence":0.093},{"start":108.436,"duration":1.4962,"confidence":0.616},{"start":109.9322,"duration":1.48946,"confidence":0.32},{"start":111.42165,"duration":1.49287,"confidence":0.615},{"start":112.91453,"duration":1.49972,"confidence":0.523},{"start":114.41425,"duration":1.49402,"confidence":0.146},{"start":115.90827,"duration":1.49267,"confidence":0.656},{"start":117.40094,"duration":1.49299,"confidence":0.506},{"start":118.89394,"duration":1.49165,"confidence":0.652},{"start":120.38559,"duration":1.49602,"confidence":0.35},{"start":121.88161,"duration":1.49671,"confidence":0.512},{"start":123.37832,"duration":1.49635,"confidence":0.202},{"start":124.87467,"duration":1.4959,"confidence":0.494},{"start":126.37057,"duration":1.49142,"confidence":0.527},{"start":127.86199,"duration":1.49303,"confidence":0.428},{"start":129.35502,"duration":1.49311,"confidence":0.678}],"beats":[{"start":0.62089,"duration":0.37432,"confidence":0.832},{"start":0.99521,"duration":0.3818,"confidence":0.63},{"start":1.37702,"duration":0.37883,"confidence":0.276},{"start":1.75584,"duration":0.37595,"confidence":0.311},{"start":2.1318,"duration":0.37695,"confidence":0.339},{"start":2.50874,"duration":0.37292,"confidence":0.237},{"start":2.88167,"duration":0.3709,"confidence":0.268},{"start":3.25257,"duration":0.37157,"confidence":0.279},{"start":3.62414,"duration":0.37191,"confidence":0.259},{"start":3.99605,"duration":0.37391,"confidence":0.195},{"start":4.36996,"duration":0.37525,"confidence":0.246},{"start":4.74521,"duration":0.37324,"confidence":0.215},{"start":5.11845,"duration":0.37491,"confidence":0.211},{"start":5.49335,"duration":0.37591,"confidence":0.217},{"start":5.86926,"duration":0.37523,"confidence":0.236},{"start":6.24448,"duration":0.37388,"confidence":0.244},{"start":6.61836,"duration":0.37487,"confidence":0.214},{"start":6.99324,"duration":0.37386,"confidence":0.233},{"start":7.36709,"duration":0.37586,"confidence":0.227},{"start":7.74295,"duration":0.37484,"confidence":0.217},{"start":8.11779,"duration":0.37483,"confidence":0.226},{"start":8.49262,"duration":0.37449,"confidence":0.274},{"start":8.86711,"duration":0.37347,"confidence":0.269},{"start":9.24058,"duration":0.37179,"confidence":0.266},{"start":9.61237,"duration":0.37312,"confidence":0.261},{"start":9.98549,"duration":0.37278,"confidence":0.322},{"start":10.35826,"duration":0.3731,"confidence":0.318},{"start":10.73136,"duration":0.37243,"confidence":0.327},{"start":11.10379,"duration":0.37309,"confidence":0.31},{"start":11.47688,"duration":0.37543,"confidence":0.348},{"start":11.85231,"duration":0.37108,"confidence":0.324},{"start":12.22339,"duration":0.37408,"confidence":0.292},{"start":12.59747,"duration":0.37609,"confidence":0.28},{"start":12.97356,"duration":0.37575,"confidence":0.198},{"start":13.3493,"duration":0.37374,"confidence":0.226},{"start":13.72304,"duration":0.3744,"confidence":0.198},{"start":14.09744,"duration":0.37172,"confidence":0.191},{"start":14.46915,"duration":0.37572,"confidence":0.201},{"start":14.84488,"duration":0.37103,"confidence":0.249},{"start":15.21591,"duration":0.37237,"confidence":0.259},{"start":15.58828,"duration":0.37236,"confidence":0.209},{"start":15.96064,"duration":0.37704,"confidence":0.255},{"start":16.33768,"duration":0.37268,"confidence":0.296},{"start":16.71036,"duration":0.37502,"confidence":0.243},{"start":17.08538,"duration":0.37635,"confidence":0.208},{"start":17.46173,"duration":0.37467,"confidence":0.139},{"start":17.8364,"duration":0.37132,"confidence":0.235},{"start":18.20772,"duration":0.37266,"confidence":0.229},{"start":18.58038,"duration":0.37165,"confidence":0.227},{"start":18.95203,"duration":0.37231,"confidence":0.193},{"start":19.32435,"duration":0.37231,"confidence":0.23},{"start":19.69666,"duration":0.37264,"confidence":0.209},{"start":20.0693,"duration":0.37565,"confidence":0.177},{"start":20.44495,"duration":0.37063,"confidence":0.116},{"start":20.81559,"duration":0.3723,"confidence":0.201},{"start":21.18789,"duration":0.3723,"confidence":0.161},{"start":21.5602,"duration":0.37464,"confidence":0.162},{"start":21.93484,"duration":0.3723,"confidence":0.165},{"start":22.30714,"duration":0.37063,"confidence":0.215},{"start":22.67778,"duration":0.37097,"confidence":0.22},{"start":23.04874,"duration":0.37732,"confidence":0.237},{"start":23.42606,"duration":0.37531,"confidence":0.218},{"start":23.80137,"duration":0.37331,"confidence":0.258},{"start":24.17468,"duration":0.37364,"confidence":0.27},{"start":24.54832,"duration":0.37531,"confidence":0.232},{"start":24.92363,"duration":0.3743,"confidence":0.227},{"start":25.29793,"duration":0.37397,"confidence":0.252},{"start":25.6719,"duration":0.37195,"confidence":0.211},{"start":26.04385,"duration":0.37462,"confidence":0.176},{"start":26.41847,"duration":0.37403,"confidence":0.153},{"start":26.7925,"duration":0.37385,"confidence":0.22},{"start":27.16635,"duration":0.37367,"confidence":0.226},{"start":27.54002,"duration":0.37449,"confidence":0.194},{"start":27.91451,"duration":0.3733,"confidence":0.313},{"start":28.28781,"duration":0.37412,"confidence":0.257},{"start":28.66193,"duration":0.37327,"confidence":0.213},{"start":29.0352,"duration":0.37467,"confidence":0.236},{"start":29.40987,"duration":0.37432,"confidence":0.287},{"start":29.78418,"duration":0.37397,"confidence":0.267},{"start":30.15815,"duration":0.37428,"confidence":0.207},{"start":30.53243,"duration":0.37461,"confidence":0.289},{"start":30.90704,"duration":0.37392,"confidence":0.3},{"start":31.28096,"duration":0.37257,"confidence":0.26},{"start":31.65353,"duration":0.37289,"confidence":0.268},{"start":32.02643,"duration":0.37288,"confidence":0.297},{"start":32.39931,"duration":0.37053,"confidence":0.272},{"start":32.76983,"duration":0.37218,"confidence":0.257},{"start":33.14202,"duration":0.37016,"confidence":0.151},{"start":33.51218,"duration":0.37617,"confidence":0.171},{"start":33.88835,"duration":0.37382,"confidence":0.165},{"start":34.26217,"duration":0.3738,"confidence":0.227},{"start":34.63598,"duration":0.37246,"confidence":0.185},{"start":35.00844,"duration":0.37346,"confidence":0.175},{"start":35.38189,"duration":0.37345,"confidence":0.173},{"start":35.75534,"duration":0.37679,"confidence":0.242},{"start":36.13213,"duration":0.37177,"confidence":0.17},{"start":36.50391,"duration":0.37278,"confidence":0.21},{"start":36.87668,"duration":0.37177,"confidence":0.237},{"start":37.24845,"duration":0.37244,"confidence":0.257},{"start":37.62089,"duration":0.37243,"confidence":0.186},{"start":37.99332,"duration":0.37142,"confidence":0.166},{"start":38.36475,"duration":0.37209,"confidence":0.151},{"start":38.73683,"duration":0.37576,"confidence":0.164},{"start":39.11259,"duration":0.37642,"confidence":0.171},{"start":39.48901,"duration":0.37542,"confidence":0.206},{"start":39.86444,"duration":0.37508,"confidence":0.18},{"start":40.23952,"duration":0.37642,"confidence":0.221},{"start":40.61594,"duration":0.37374,"confidence":0.135},{"start":40.98968,"duration":0.37274,"confidence":0.194},{"start":41.36242,"duration":0.37007,"confidence":0.2},{"start":41.73249,"duration":0.37274,"confidence":0.19},{"start":42.10523,"duration":0.3714,"confidence":0.115},{"start":42.47663,"duration":0.37374,"confidence":0.164},{"start":42.85036,"duration":0.37574,"confidence":0.128},{"start":43.2261,"duration":0.37708,"confidence":0.133},{"start":43.60318,"duration":0.3744,"confidence":0.083},{"start":43.97758,"duration":0.37507,"confidence":0.128},{"start":44.35265,"duration":0.37206,"confidence":0.167},{"start":44.72471,"duration":0.37373,"confidence":0.22},{"start":45.09844,"duration":0.37373,"confidence":0.155},{"start":45.47216,"duration":0.37239,"confidence":0.209},{"start":45.84455,"duration":0.37171,"confidence":0.243},{"start":46.21626,"duration":0.37405,"confidence":0.175},{"start":46.59031,"duration":0.37204,"confidence":0.195},{"start":46.96234,"duration":0.37237,"confidence":0.275},{"start":47.33471,"duration":0.3757,"confidence":0.275},{"start":47.71041,"duration":0.37603,"confidence":0.271},{"start":48.08645,"duration":0.37436,"confidence":0.236},{"start":48.46081,"duration":0.37603,"confidence":0.254},{"start":48.83684,"duration":0.37469,"confidence":0.251},{"start":49.21154,"duration":0.37403,"confidence":0.233},{"start":49.58556,"duration":0.37436,"confidence":0.213},{"start":49.95992,"duration":0.37269,"confidence":0.262},{"start":50.33261,"duration":0.37001,"confidence":0.22},{"start":50.70262,"duration":0.37369,"confidence":0.196},{"start":51.07631,"duration":0.37302,"confidence":0.176},{"start":51.44933,"duration":0.37135,"confidence":0.179},{"start":51.82068,"duration":0.37168,"confidence":0.214},{"start":52.19236,"duration":0.37335,"confidence":0.232},{"start":52.56572,"duration":0.37836,"confidence":0.152},{"start":52.94408,"duration":0.37936,"confidence":0.174},{"start":53.32344,"duration":0.37235,"confidence":0.164},{"start":53.69579,"duration":0.37468,"confidence":0.178},{"start":54.07047,"duration":0.37736,"confidence":0.115},{"start":54.44783,"duration":0.37702,"confidence":0.17},{"start":54.82485,"duration":0.37502,"confidence":0.168},{"start":55.19986,"duration":0.36967,"confidence":0.221},{"start":55.56953,"duration":0.36666,"confidence":0.147},{"start":55.93619,"duration":0.37133,"confidence":0.222},{"start":56.30753,"duration":0.37066,"confidence":0.216},{"start":56.67819,"duration":0.37133,"confidence":0.317},{"start":57.04952,"duration":0.37165,"confidence":0.291},{"start":57.42117,"duration":0.37231,"confidence":0.369},{"start":57.79348,"duration":0.37197,"confidence":0.341},{"start":58.16545,"duration":0.37564,"confidence":0.384},{"start":58.54109,"duration":0.37497,"confidence":0.35},{"start":58.91606,"duration":0.37396,"confidence":0.4},{"start":59.29001,"duration":0.33703,"confidence":0.374},{"start":59.62704,"duration":0.337,"confidence":0.374},{"start":59.96404,"duration":0.33696,"confidence":0.374},{"start":60.301,"duration":0.33693,"confidence":0.374},{"start":60.63794,"duration":0.3369,"confidence":0.374},{"start":60.97484,"duration":0.3738,"confidence":0.374},{"start":61.34864,"duration":0.37413,"confidence":0.377},{"start":61.72277,"duration":0.37346,"confidence":0.341},{"start":62.09624,"duration":0.3748,"confidence":0.337},{"start":62.47104,"duration":0.37447,"confidence":0.356},{"start":62.84551,"duration":0.37347,"confidence":0.396},{"start":63.21897,"duration":0.37313,"confidence":0.396},{"start":63.59211,"duration":0.37414,"confidence":0.429},{"start":63.96624,"duration":0.3738,"confidence":0.477},{"start":64.34005,"duration":0.3738,"confidence":0.494},{"start":64.71385,"duration":0.37381,"confidence":0.449},{"start":65.08766,"duration":0.37247,"confidence":0.447},{"start":65.46013,"duration":0.37281,"confidence":0.459},{"start":65.83294,"duration":0.37315,"confidence":0.462},{"start":66.20609,"duration":0.37148,"confidence":0.407},{"start":66.57756,"duration":0.37182,"confidence":0.452},{"start":66.94938,"duration":0.37249,"confidence":0.455},{"start":67.32187,"duration":0.37249,"confidence":0.438},{"start":67.69436,"duration":0.37316,"confidence":0.365},{"start":68.06751,"duration":0.37383,"confidence":0.382},{"start":68.44134,"duration":0.37349,"confidence":0.395},{"start":68.81483,"duration":0.37517,"confidence":0.426},{"start":69.19,"duration":0.37517,"confidence":0.453},{"start":69.56517,"duration":0.37491,"confidence":0.483},{"start":69.94008,"duration":0.37474,"confidence":0.494},{"start":70.31482,"duration":0.37557,"confidence":0.513},{"start":70.6904,"duration":0.3744,"confidence":0.518},{"start":71.0648,"duration":0.37389,"confidence":0.534},{"start":71.43869,"duration":0.37439,"confidence":0.54},{"start":71.81307,"duration":0.37488,"confidence":0.496},{"start":72.18795,"duration":0.37396,"confidence":0.517},{"start":72.56191,"duration":0.37329,"confidence":0.504},{"start":72.93521,"duration":0.37329,"confidence":0.549},{"start":73.3085,"duration":0.37529,"confidence":0.523},{"start":73.68379,"duration":0.37529,"confidence":0.53},{"start":74.05908,"duration":0.37229,"confidence":0.543},{"start":74.43137,"duration":0.37228,"confidence":0.547},{"start":74.80365,"duration":0.37295,"confidence":0.521},{"start":75.1766,"duration":0.37228,"confidence":0.531},{"start":75.54888,"duration":0.37161,"confidence":0.522},{"start":75.92049,"duration":0.37127,"confidence":0.546},{"start":76.29176,"duration":0.37127,"confidence":0.54},{"start":76.66303,"duration":0.37327,"confidence":0.534},{"start":77.0363,"duration":0.37494,"confidence":0.553},{"start":77.41124,"duration":0.3736,"confidence":0.547},{"start":77.78484,"duration":0.3756,"confidence":0.508},{"start":78.16044,"duration":0.3756,"confidence":0.436},{"start":78.53604,"duration":0.37493,"confidence":0.372},{"start":78.91097,"duration":0.37526,"confidence":0.408},{"start":79.28623,"duration":0.37593,"confidence":0.438},{"start":79.66215,"duration":0.37359,"confidence":0.428},{"start":80.03574,"duration":0.37459,"confidence":0.404},{"start":80.41033,"duration":0.37392,"confidence":0.4},{"start":80.78426,"duration":0.37493,"confidence":0.434},{"start":81.15919,"duration":0.37493,"confidence":0.398},{"start":81.53412,"duration":0.3746,"confidence":0.399},{"start":81.90873,"duration":0.37327,"confidence":0.441},{"start":82.282,"duration":0.37261,"confidence":0.464},{"start":82.65461,"duration":0.37429,"confidence":0.442},{"start":83.02889,"duration":0.37462,"confidence":0.438},{"start":83.40352,"duration":0.37362,"confidence":0.419},{"start":83.77714,"duration":0.37329,"confidence":0.427},{"start":84.15043,"duration":0.37463,"confidence":0.392},{"start":84.52506,"duration":0.37496,"confidence":0.428},{"start":84.90003,"duration":0.37529,"confidence":0.436},{"start":85.27532,"duration":0.37362,"confidence":0.481},{"start":85.64894,"duration":0.37362,"confidence":0.463},{"start":86.02257,"duration":0.37429,"confidence":0.473},{"start":86.39686,"duration":0.37462,"confidence":0.452},{"start":86.77148,"duration":0.37362,"confidence":0.472},{"start":87.1451,"duration":0.37261,"confidence":0.453},{"start":87.51771,"duration":0.37295,"confidence":0.453},{"start":87.89066,"duration":0.37328,"confidence":0.457},{"start":88.26394,"duration":0.37194,"confidence":0.48},{"start":88.63587,"duration":0.37194,"confidence":0.442},{"start":89.00781,"duration":0.37193,"confidence":0.446},{"start":89.37975,"duration":0.3726,"confidence":0.424},{"start":89.75235,"duration":0.37394,"confidence":0.403},{"start":90.12628,"duration":0.3746,"confidence":0.397},{"start":90.50088,"duration":0.37359,"confidence":0.421},{"start":90.87446,"duration":0.37491,"confidence":0.456},{"start":91.24937,"duration":0.37389,"confidence":0.457},{"start":91.62326,"duration":0.3732,"confidence":0.46},{"start":91.99646,"duration":0.37418,"confidence":0.467},{"start":92.37064,"duration":0.37315,"confidence":0.429},{"start":92.74379,"duration":0.3728,"confidence":0.38},{"start":93.11659,"duration":0.37344,"confidence":0.384},{"start":93.49003,"duration":0.37309,"confidence":0.394},{"start":93.86312,"duration":0.37374,"confidence":0.446},{"start":94.23686,"duration":0.37572,"confidence":0.459},{"start":94.61258,"duration":0.37403,"confidence":0.464},{"start":94.98661,"duration":0.37368,"confidence":0.441},{"start":95.36029,"duration":0.37366,"confidence":0.414},{"start":95.73395,"duration":0.37364,"confidence":0.437},{"start":96.10759,"duration":0.3743,"confidence":0.425},{"start":96.48189,"duration":0.37363,"confidence":0.438},{"start":96.85551,"duration":0.37095,"confidence":0.466},{"start":97.22647,"duration":0.37128,"confidence":0.466},{"start":97.59775,"duration":0.37195,"confidence":0.478},{"start":97.9697,"duration":0.37195,"confidence":0.477},{"start":98.34165,"duration":0.37228,"confidence":0.452},{"start":98.71393,"duration":0.37228,"confidence":0.434},{"start":99.08621,"duration":0.37195,"confidence":0.425},{"start":99.45815,"duration":0.37362,"confidence":0.397},{"start":99.83177,"duration":0.37428,"confidence":0.408},{"start":100.20606,"duration":0.37529,"confidence":0.402},{"start":100.58134,"duration":0.37529,"confidence":0.408},{"start":100.95663,"duration":0.37562,"confidence":0.345},{"start":101.33224,"duration":0.37595,"confidence":0.349},{"start":101.70819,"duration":0.37561,"confidence":0.337},{"start":102.0838,"duration":0.37626,"confidence":0.355},{"start":102.46006,"duration":0.37525,"confidence":0.326},{"start":102.83531,"duration":0.37324,"confidence":0.365},{"start":103.20855,"duration":0.37389,"confidence":0.361},{"start":103.58244,"duration":0.37321,"confidence":0.399},{"start":103.95565,"duration":0.37353,"confidence":0.433},{"start":104.32918,"duration":0.37386,"confidence":0.417},{"start":104.70304,"duration":0.37318,"confidence":0.422},{"start":105.07622,"duration":0.37251,"confidence":0.438},{"start":105.44873,"duration":0.37351,"confidence":0.434},{"start":105.82224,"duration":0.37216,"confidence":0.39},{"start":106.1944,"duration":0.37215,"confidence":0.358},{"start":106.56654,"duration":0.37147,"confidence":0.344},{"start":106.93802,"duration":0.37378,"confidence":0.336},{"start":107.3118,"duration":0.37343,"confidence":0.291},{"start":107.68522,"duration":0.37541,"confidence":0.266},{"start":108.06063,"duration":0.37537,"confidence":0.255},{"start":108.436,"duration":0.37501,"confidence":0.192},{"start":108.81102,"duration":0.37431,"confidence":0.248},{"start":109.18533,"duration":0.37428,"confidence":0.327},{"start":109.55961,"duration":0.37259,"confidence":0.393},{"start":109.9322,"duration":0.37356,"confidence":0.419},{"start":110.30576,"duration":0.37154,"confidence":0.423},{"start":110.6773,"duration":0.37152,"confidence":0.422},{"start":111.04882,"duration":0.37283,"confidence":0.406},{"start":111.42165,"duration":0.37349,"confidence":0.43},{"start":111.79514,"duration":0.37281,"confidence":0.444},{"start":112.16795,"duration":0.37379,"confidence":0.43},{"start":112.54174,"duration":0.37278,"confidence":0.382},{"start":112.91453,"duration":0.37444,"confidence":0.299},{"start":113.28897,"duration":0.37543,"confidence":0.277},{"start":113.6644,"duration":0.37443,"confidence":0.238},{"start":114.03883,"duration":0.37542,"confidence":0.244},{"start":114.41425,"duration":0.37442,"confidence":0.204},{"start":114.78868,"duration":0.37376,"confidence":0.269},{"start":115.16243,"duration":0.37342,"confidence":0.329},{"start":115.53585,"duration":0.37242,"confidence":0.392},{"start":115.90827,"duration":0.37375,"confidence":0.406},{"start":116.28202,"duration":0.37342,"confidence":0.415},{"start":116.65544,"duration":0.37342,"confidence":0.415},{"start":117.02886,"duration":0.37208,"confidence":0.365},{"start":117.40094,"duration":0.37375,"confidence":0.308},{"start":117.77469,"duration":0.37408,"confidence":0.312},{"start":118.14877,"duration":0.37308,"confidence":0.287},{"start":118.52186,"duration":0.37208,"confidence":0.295},{"start":118.89394,"duration":0.37208,"confidence":0.256},{"start":119.26601,"duration":0.37075,"confidence":0.225},{"start":119.63676,"duration":0.37508,"confidence":0.192},{"start":120.01184,"duration":0.37375,"confidence":0.229},{"start":120.38559,"duration":0.37342,"confidence":0.234},{"start":120.759,"duration":0.37409,"confidence":0.238},{"start":121.13309,"duration":0.37309,"confidence":0.303},{"start":121.50618,"duration":0.37542,"confidence":0.315},{"start":121.88161,"duration":0.37576,"confidence":0.287},{"start":122.25737,"duration":0.37443,"confidence":0.316},{"start":122.63179,"duration":0.3731,"confidence":0.334},{"start":123.00489,"duration":0.37343,"confidence":0.328},{"start":123.37832,"duration":0.37409,"confidence":0.323},{"start":123.75241,"duration":0.37409,"confidence":0.294},{"start":124.1265,"duration":0.37442,"confidence":0.272},{"start":124.50092,"duration":0.37375,"confidence":0.234},{"start":124.87467,"duration":0.37341,"confidence":0.231},{"start":125.24807,"duration":0.37406,"confidence":0.245},{"start":125.62214,"duration":0.37406,"confidence":0.291},{"start":125.99619,"duration":0.37438,"confidence":0.344},{"start":126.37057,"duration":0.37337,"confidence":0.373},{"start":126.74393,"duration":0.37169,"confidence":0.412},{"start":127.11562,"duration":0.37268,"confidence":0.422},{"start":127.48831,"duration":0.37368,"confidence":0.415},{"start":127.86199,"duration":0.37401,"confidence":0.394},{"start":128.23599,"duration":0.374,"confidence":0.366},{"start":128.61,"duration":0.37301,"confidence":0.315},{"start":128.983,"duration":0.37201,"confidence":0.308},{"start":129.35502,"duration":0.37368,"confidence":0.28},{"start":129.7287,"duration":0.37536,"confidence":0.263},{"start":130.10406,"duration":0.37222,"confidence":0.205},{"start":130.47628,"duration":0.37184,"confidence":0.128},{"start":130.84812,"duration":0.38539,"confidence":0.083},{"start":131.23351,"duration":0.38539,"confidence":0.091}],"sections":[{"start":0,"duration":28.28781,"confidence":1,"loudness":-27.322,"tempo":160.565,"tempo_confidence":0.25,"key":7,"key_confidence":0.672,"mode":0,"mode_confidence":0.466,"time_signature":4,"time_signature_confidence":1},{"start":28.28781,"duration":19.4226,"confidence":0.279,"loudness":-26.605,"tempo":160.724,"tempo_confidence":0.204,"key":10,"key_confidence":0.33,"mode":1,"mode_confidence":0.318,"time_signature":4,"time_signature_confidence":1},{"start":47.71041,"duration":29.70083,"confidence":0.681,"loudness":-22.596,"tempo":160.614,"tempo_confidence":0.372,"key":7,"key_confidence":0.645,"mode":0,"mode_confidence":0.454,"time_signature":4,"time_signature_confidence":1},{"start":77.41124,"duration":60.26667,"confidence":1,"loudness":-11.656,"tempo":160.636,"tempo_confidence":0.372,"key":7,"key_confidence":0.437,"mode":0,"mode_confidence":0.332,"time_signature":4,"time_signature_confidence":0.979}]}}}} -------------------------------------------------------------------------------- /demos/cc_audio/encom_part_ii.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geelen/x-gif/085b0c79ff119288689b0a49653b4a1a5c95b645/demos/cc_audio/encom_part_ii.mp3 -------------------------------------------------------------------------------- /demos/cc_audio/encom_part_ii.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geelen/x-gif/085b0c79ff119288689b0a49653b4a1a5c95b645/demos/cc_audio/encom_part_ii.ogg -------------------------------------------------------------------------------- /demos/exploded.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exploded 5 | 6 | 7 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /demos/full-screen.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Toggle Button 5 | 6 | 7 | 10 | 11 | 12 | 13 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /demos/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Toggle Button 8 | 9 | 10 | 12 | 13 | 14 | 15 | AUDIO 16 | BASIC 17 | BPM 18 | EXPLODED 19 | FULL SCREEN 20 | N TIMES 21 | PING PONG 22 | SPEED 23 | STOPPABLE 24 | 25 | 26 | -------------------------------------------------------------------------------- /demos/n-times.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Toggle Button 5 | 6 | 7 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /demos/ping-pong.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Toggle Button 5 | 6 | 7 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 |
18 | 19 | 20 |
21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /demos/speed.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Toggle Button 5 | 6 | 7 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /demos/stoppable.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Toggle Button 5 | 15 | 16 | 17 | 18 |

Click a gif to stop or start it

19 | 20 | 21 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /demos/synched_playback.js: -------------------------------------------------------------------------------- 1 | var setupAudioSynching = function (audio, xGifs, metadata) { 2 | var synchOffset = -0.1; 3 | 4 | audio.addEventListener('playing', function () { 5 | var beats = metadata.response.track.analysis.beats, 6 | beatIndex = 0; 7 | while (beats[0].start > 0) { 8 | beats.unshift({ 9 | start: beats[0].start - beats[0].duration, 10 | duration: beats[0].duration, 11 | confidence: 0 12 | }) 13 | } 14 | 15 | var animationLoop = function () { 16 | requestAnimationFrame(animationLoop); 17 | 18 | if (beats.length > beatIndex) { 19 | var currentTime = audio.currentTime + synchOffset; 20 | while (beatIndex < beats.length && currentTime > beats[beatIndex].start) { 21 | beatIndex++; 22 | } 23 | var beat = beats[beatIndex - 1]; 24 | 25 | var sinceLastBeat = currentTime - beat.start, 26 | beatFraction = sinceLastBeat / beat.duration; 27 | [].forEach.call(xGifs, function (xGif) { 28 | xGif.clock(beatIndex, beat.duration * 1000 / audio.playbackRate, beatFraction); 29 | }) 30 | } 31 | } 32 | animationLoop(); 33 | }); 34 | } 35 | -------------------------------------------------------------------------------- /demos/visual_test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | X-GIF MULTIBALL 5 | 15 | 16 | 18 | 19 | 20 |

Any of these dun broken?

21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /dist/layout.html: -------------------------------------------------------------------------------- 1 | 9 | -------------------------------------------------------------------------------- /dist/polymer.html: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /dist/x-gif.angular.js: -------------------------------------------------------------------------------- 1 | !function t(e,r,n){function i(u,s){if(!r[u]){if(!e[u]){var a="function"==typeof require&&require;if(!s&&a)return a(u,!0);if(o)return o(u,!0);throw new Error("Cannot find module '"+u+"'")}var c=r[u]={exports:{}};e[u][0].call(c.exports,function(t){var r=e[u][1][t];return i(r?r:t)},c,c.exports,t,e,r,n)}return r[u].exports}for(var o="function"==typeof require&&require,u=0;u3?("function"==typeof n&&(t.__proto__=n),t.prototype=l(u(n),i(e))):t.prototype=e,h(t,"prototype",{configurable:!1,writable:!1}),f(t,i(r))}function u(t){if("function"==typeof t){var e=t.prototype;if(a(e)===e||null===e)return t.prototype}if(null===t)return null;throw new c}function s(t,r,n){null!==d(r)&&e(t,r,"constructor",n)}var a=Object,c=TypeError,l=a.create,f=$traceurRuntime.defineProperties,h=$traceurRuntime.defineProperty,p=$traceurRuntime.getOwnPropertyDescriptor,m=$traceurRuntime.getOwnPropertyNames,d=Object.getPrototypeOf;$traceurRuntime.createClass=o,$traceurRuntime.defaultSuperCall=s,$traceurRuntime.superCall=e,$traceurRuntime.superGet=r,$traceurRuntime.superSet=n}(),function(){"use strict";function t(t){return{configurable:!0,enumerable:!1,value:t,writable:!0}}function e(t){return new Error("Traceur compiler bug: invalid state in state machine: "+t)}function r(){this.state=0,this.GState=g,this.storedException=void 0,this.finallyFallThrough=void 0,this.sent_=void 0,this.returnValue=void 0,this.tryStack_=[]}function n(t,e,r,n){switch(t.GState){case y:throw new Error('"'+r+'" on executing generator');case j:if("next"==r)return{value:void 0,done:!0};throw new Error('"'+r+'" on closed generator');case g:if("throw"===r)throw t.GState=j,n;if(void 0!==n)throw v("Sent value to newborn generator");case b:t.GState=y,t.action=r,t.sent=n;var i=e(t),o=i===t;return o&&(i=t.returnValue),t.GState=o?j:b,{value:i,done:o}}}function i(){}function o(){}function u(t,e,n){var i=l(t,n),o=new r,u=d(e.prototype);return u[x]=o,u[_]=i,u}function s(t){return t.prototype=d(o.prototype),t.__proto__=o,t}function a(){r.call(this),this.err=void 0;var t=this;t.result=new Promise(function(e,r){t.resolve=e,t.reject=r})}function c(t,e){var r=l(t,e),n=new a;return n.createCallback=function(t){return function(e){n.state=t,n.value=e,r(n)}},n.errback=function(t){f(n,t),r(n)},r(n),n.result}function l(t,e){return function(r){for(;;)try{return t.call(e,r)}catch(n){f(r,n)}}}function f(t,e){t.storedException=e;var r=t.tryStack_[t.tryStack_.length-1];return r?(t.state=void 0!==r.catch?r.catch:r.finally,void(void 0!==r.finallyFallThrough&&(t.finallyFallThrough=r.finallyFallThrough))):void t.handleException(e)}var h=$traceurRuntime.createPrivateName,p=$traceurRuntime.defineProperties,m=$traceurRuntime.defineProperty,d=Object.create,v=TypeError,g=0,y=1,b=2,j=3,w=-2,O=-3;r.prototype={pushTry:function(t,e){if(null!==e){for(var r=null,n=this.tryStack_.length-1;n>=0;n--)if(void 0!==this.tryStack_[n].catch){r=this.tryStack_[n].catch;break}null===r&&(r=O),this.tryStack_.push({"finally":e,finallyFallThrough:r})}null!==t&&this.tryStack_.push({"catch":t})},popTry:function(){this.tryStack_.pop()},get sent(){return this.maybeThrow(),this.sent_},set sent(t){this.sent_=t},get sentIgnoreThrow(){return this.sent_},maybeThrow:function(){if("throw"===this.action)throw this.action="next",this.sent_},end:function(){switch(this.state){case w:return this;case O:throw this.storedException;default:throw e(this.state)}},handleException:function(t){throw this.GState=j,this.state=w,t}};var x=h(),_=h();i.prototype=o,m(o,"constructor",t(i)),o.prototype={constructor:o,next:function(t){return n(this[x],this[_],"next",t)},"throw":function(t){return n(this[x],this[_],"throw",t)}},p(o.prototype,{constructor:{enumerable:!1},next:{enumerable:!1},"throw":{enumerable:!1}}),Object.defineProperty(o.prototype,Symbol.iterator,t(function(){return this})),a.prototype=d(r.prototype),a.prototype.end=function(){switch(this.state){case w:this.resolve(this.returnValue);break;case O:this.reject(this.storedException);break;default:this.reject(e(this.state))}},a.prototype.handleException=function(){this.state=O},$traceurRuntime.asyncWrap=c,$traceurRuntime.initGeneratorFunction=s,$traceurRuntime.createGeneratorInstance=u}(),function(){function t(t,e,r,n,i,o,u){var s=[];return t&&s.push(t,":"),r&&(s.push("//"),e&&s.push(e,"@"),s.push(r),n&&s.push(":",n)),i&&s.push(i),o&&s.push("?",o),u&&s.push("#",u),s.join("")}function e(t){return t.match(s)}function r(t){if("/"===t)return"/";for(var e="/"===t[0]?"/":"",r="/"===t.slice(-1)?"/":"",n=t.split("/"),i=[],o=0,u=0;u0;)i.unshift("..");0===i.length&&i.push(".")}return e+i.join("/")+r}function n(e){var n=e[a.PATH]||"";return n=r(n),e[a.PATH]=n,t(e[a.SCHEME],e[a.USER_INFO],e[a.DOMAIN],e[a.PORT],e[a.PATH],e[a.QUERY_DATA],e[a.FRAGMENT])}function i(t){var r=e(t);return n(r)}function o(t,r){var i=e(r),o=e(t);if(i[a.SCHEME])return n(i);i[a.SCHEME]=o[a.SCHEME];for(var u=a.SCHEME;u<=a.PORT;u++)i[u]||(i[u]=o[u]);if("/"==i[a.PATH][0])return n(i);var s=o[a.PATH],c=s.lastIndexOf("/");return s=s.slice(0,c+1)+i[a.PATH],i[a.PATH]=s,n(i)}function u(t){if(!t)return!1;if("/"===t[0])return!0;var r=e(t);return r[a.SCHEME]?!0:!1}var s=new RegExp("^(?:([^:/?#.]+):)?(?://(?:([^/?#]*)@)?([\\w\\d\\-\\u0100-\\uffff.%]*)(?::([0-9]+))?)?([^?#]+)?(?:\\?([^#]*))?(?:#(.*))?$"),a={SCHEME:1,USER_INFO:2,DOMAIN:3,PORT:4,PATH:5,QUERY_DATA:6,FRAGMENT:7};$traceurRuntime.canonicalizeUrl=i,$traceurRuntime.isAbsolute=u,$traceurRuntime.removeDotSegments=r,$traceurRuntime.resolveUrl=o}(),function(t){"use strict";function e(t){if(t){var e=m.normalize(t);return a[e]}}function r(t){var e=arguments[1],r=Object.create(null);return Object.getOwnPropertyNames(t).forEach(function(n){var i,o;if(e===p){var u=Object.getOwnPropertyDescriptor(t,n);u.get&&(i=u.get)}i||(o=t[n],i=function(){return o}),Object.defineProperty(r,n,{get:i,enumerable:!0})}),Object.preventExtensions(r),r}var n,i=$traceurRuntime.assertObject($traceurRuntime),o=i.canonicalizeUrl,u=i.resolveUrl,s=i.isAbsolute,a=Object.create(null);n=t.location&&t.location.href?u(t.location.href,"./"):"";var c=function(t,e){this.url=t,this.value_=e};$traceurRuntime.createClass(c,{},{});var l=function(t,e){$traceurRuntime.superCall(this,f.prototype,"constructor",[t,null]),this.func=e},f=l;$traceurRuntime.createClass(l,{getUncoatedModule:function(){return this.value_?this.value_:this.value_=this.func.call(t)}},{},c);var h=Object.create(null),p={},m={normalize:function(t,e){if("string"!=typeof t)throw new TypeError("module name must be a string, not "+typeof t);if(s(t))return o(t);if(/[^\.]\/\.\.\//.test(t))throw new Error("module name embeds /../: "+t);return"."===t[0]&&e?u(e,t):o(t)},get:function(t){var n=e(t);if(!n)return void 0;var i=h[n.url];return i?i:(i=r(n.getUncoatedModule(),p),h[n.url]=i)},set:function(t,e){t=String(t),a[t]=new l(t,function(){return e}),h[t]=e},get baseURL(){return n},set baseURL(t){n=String(t)},registerModule:function(t,e){var r=m.normalize(t);if(a[r])throw new Error("duplicate module named "+r);a[r]=new l(r,e)},bundleStore:Object.create(null),register:function(t,e,r){e&&(e.length||r.length)?this.bundleStore[t]={deps:e,execute:function(){var t=arguments,n={};e.forEach(function(e,r){return n[e]=t[r]});var i=r.call(this,n);return i.execute.call(this),i.exports}}:this.registerModule(t,r)},getAnonymousModule:function(e){return new r(e.call(t),p)},getForTesting:function(t){var e=this;return this.testingPrefix_||Object.keys(h).some(function(t){var r=/(traceur@[^\/]*\/)/.exec(t);return r?(e.testingPrefix_=r[1],!0):void 0}),this.get(this.testingPrefix_+t)}};m.set("@traceur/src/runtime/ModuleStore",new r({ModuleStore:m}));var d=$traceurRuntime.setupGlobals;$traceurRuntime.setupGlobals=function(t){d(t)},$traceurRuntime.ModuleStore=m,t.System={register:m.register.bind(m),get:m.get,set:m.set,normalize:m.normalize},$traceurRuntime.getModuleImpl=function(t){var r=e(t);return r&&r.getUncoatedModule()}}("undefined"!=typeof e?e:this),System.register("traceur-runtime@0.0.42/src/runtime/polyfills/utils",[],function(){"use strict";function t(t){return 0|t}function e(t){return t&&("object"==typeof t||"function"==typeof t)}function r(t){return"function"==typeof t}function n(t){return t=+t,isNaN(t)?0:isFinite(t)&&0!==t?t>0?Math.floor(t):Math.ceil(t):t}function i(t){var e=n(t);return 0>e?0:Math.min(e,u)}var o=$traceurRuntime.toObject,u=Math.pow(2,53)-1;return{get toObject(){return o},get toUint32(){return t},get isObject(){return e},get isCallable(){return r},get toInteger(){return n},get toLength(){return i}}}),System.register("traceur-runtime@0.0.42/src/runtime/polyfills/Array",[],function(){"use strict";function t(t){var e=void 0!==arguments[1]?arguments[1]:0,r=arguments[2],n=s(this),i=u(n.length),a=o(e),c=void 0!==r?o(r):i;for(a=0>a?Math.max(i+a,0):Math.min(a,i),c=0>c?Math.max(i+c,0):Math.min(c,i);c>a;)n[a]=t,a++;return n}function e(t){var e=arguments[1];return n(this,t,e)}function r(t){var e=arguments[1];return n(this,t,e,!0)}function n(t,e){var r=arguments[2],n=void 0!==arguments[3]?arguments[3]:!1,i=s(t),o=u(i.length);if(!a(e))throw TypeError();for(var c=0;o>c;c++)if(c in i){var l=i[c];if(e.call(r,l,c,i))return n?c:l}return n?-1:void 0}var i=$traceurRuntime.assertObject(System.get("traceur-runtime@0.0.42/src/runtime/polyfills/utils")),o=i.toInteger,u=i.toLength,s=i.toObject,a=i.isCallable;return{get fill(){return t},get find(){return e},get findIndex(){return r}}}),System.register("traceur-runtime@0.0.42/src/runtime/polyfills/ArrayIterator",[],function(){"use strict";function t(t,e){var r=s(t),n=new h;return n.iteratorObject_=r,n.arrayIteratorNextIndex_=0,n.arrayIterationKind_=e,n}function e(t,e){return{value:t,done:e}}function r(){return t(this,f)}function n(){return t(this,c)}function i(){return t(this,l)}var o,u=$traceurRuntime.assertObject(System.get("traceur-runtime@0.0.42/src/runtime/polyfills/utils")),s=u.toObject,a=u.toUint32,c=1,l=2,f=3,h=function(){};return $traceurRuntime.createClass(h,(o={},Object.defineProperty(o,"next",{value:function(){var t=s(this),r=t.iteratorObject_;if(!r)throw new TypeError("Object is not an ArrayIterator");var n=t.arrayIteratorNextIndex_,i=t.arrayIterationKind_,o=a(r.length);return n>=o?(t.arrayIteratorNextIndex_=1/0,e(void 0,!0)):(t.arrayIteratorNextIndex_=n+1,i==l?e(r[n],!1):i==f?e([n,r[n]],!1):e(n,!1))},configurable:!0,enumerable:!0,writable:!0}),Object.defineProperty(o,Symbol.iterator,{value:function(){return this},configurable:!0,enumerable:!0,writable:!0}),o),{}),{get entries(){return r},get keys(){return n},get values(){return i}}}),System.register("traceur-runtime@0.0.42/src/runtime/polyfills/Map",[],function(){"use strict";function t(t,e){if(r(e)){var i=n(e);return i&&t.objectIndex_[i.hash]}return"string"==typeof e?t.stringIndex_[e]:t.primitiveIndex_[e]}function e(t){t.entries_=[],t.objectIndex_=Object.create(null),t.stringIndex_=Object.create(null),t.primitiveIndex_=Object.create(null),t.deletedCount_=0}var r=$traceurRuntime.assertObject(System.get("traceur-runtime@0.0.42/src/runtime/polyfills/utils")).isObject,n=$traceurRuntime.getOwnHashObject,i=Object.prototype.hasOwnProperty,o={},u=function(){var t=arguments[0];if(!r(this))throw new TypeError("Constructor Map requires 'new'");if(i.call(this,"entries_"))throw new TypeError("Map can not be reentrantly initialised");if(e(this),null!==t&&void 0!==t){var n=t[Symbol.iterator];if(void 0!==n)for(var o,u=t[Symbol.iterator]();!(o=u.next()).done;){var s=$traceurRuntime.assertObject(o.value),a=s[0],c=s[1];this.set(a,c)}}};return $traceurRuntime.createClass(u,{get size(){return this.entries_.length/2-this.deletedCount_},get:function(e){var r=t(this,e);return void 0!==r?this.entries_[r+1]:void 0},set:function(e,i){var o=r(e),u="string"==typeof e,s=t(this,e);if(void 0!==s)this.entries_[s+1]=i;else if(s=this.entries_.length,this.entries_[s]=e,this.entries_[s+1]=i,o){var a=n(e),c=a.hash;this.objectIndex_[c]=s}else u?this.stringIndex_[e]=s:this.primitiveIndex_[e]=s;return this},has:function(e){return void 0!==t(this,e)},"delete":function(t){var e,i,u=r(t),s="string"==typeof t;if(u){var a=n(t);a&&(e=this.objectIndex_[i=a.hash],delete this.objectIndex_[i])}else s?(e=this.stringIndex_[t],delete this.stringIndex_[t]):(e=this.primitiveIndex_[t],delete this.primitiveIndex_[t]);void 0!==e&&(this.entries_[e]=o,this.entries_[e+1]=void 0,this.deletedCount_++)},clear:function(){e(this)},forEach:function(t){for(var e=arguments[1],r=0,n=this.entries_.length;n>r;r+=2){var i=this.entries_[r],u=this.entries_[r+1];i!==o&&t.call(e,u,i,this)}}},{}),{get Map(){return u}}}),System.register("traceur-runtime@0.0.42/src/runtime/polyfills/Object",[],function(){"use strict";function t(t,e){return t===e?0!==t||1/t===1/e:t!==t&&e!==e}function e(t){for(var e=1;er;r++){var u=i[r];a[u]||(t[u]=n[u])}}return t}function r(t,e){var r,n,s=u(e),c=s.length;for(r=0;c>r;r++){var l=s[r];a[l]||(n=o(e,s[r]),i(t,s[r],n))}return t}var n=$traceurRuntime.assertObject(System.get("traceur-runtime@0.0.42/src/runtime/polyfills/utils")),n=(n.toInteger,n.toLength,n.toObject,n.isCallable,$traceurRuntime.assertObject($traceurRuntime)),i=n.defineProperty,o=n.getOwnPropertyDescriptor,u=n.getOwnPropertyNames,s=n.keys,a=n.privateNames;return{get is(){return t},get assign(){return e},get mixin(){return r}}}),System.register("traceur-runtime@0.0.42/node_modules/rsvp/lib/rsvp/asap",[],function(){"use strict";function e(){return function(){t.nextTick(i)}}function r(){var t=0,e=new a(i),r=document.createTextNode("");return e.observe(r,{characterData:!0}),function(){r.data=t=++t%2}}function n(){return function(){setTimeout(i,1)}}function i(){for(var t=0;t1?arguments[1]:void 0),o=i?Number(i):0;isNaN(o)&&(o=0);var u=Math.min(Math.max(o,0),r);return a.call(e,n,o)==u}function e(t){var e=String(this);if(null==this||"[object RegExp]"==s.call(t))throw TypeError();var r=e.length,n=String(t),i=n.length,o=r;if(arguments.length>1){var u=arguments[1];void 0!==u&&(o=u?Number(u):0,isNaN(o)&&(o=0))}var a=Math.min(Math.max(o,0),r),l=a-i;return 0>l?!1:c.call(e,n,l)==l}function r(t){if(null==this)throw TypeError();var e=String(this),r=e.length,n=String(t),i=(n.length,arguments.length>1?arguments[1]:void 0),o=i?Number(i):0;isNaN(o)&&(o=0);Math.min(Math.max(o,0),r);return-1!=a.call(e,n,o)}function n(t){if(null==this)throw TypeError();var e=String(this),r=t?Number(t):0;if(isNaN(r)&&(r=0),0>r||1/0==r)throw RangeError();if(0==r)return"";for(var n="";r--;)n+=e;return n}function i(t){if(null==this)throw TypeError();var e=String(this),r=e.length,n=t?Number(t):0;if(isNaN(n)&&(n=0),0>n||n>=r)return void 0;var i,o=e.charCodeAt(n);return o>=55296&&56319>=o&&r>n+1&&(i=e.charCodeAt(n+1),i>=56320&&57343>=i)?1024*(o-55296)+i-56320+65536:o}function o(t){var e=t.raw,r=e.length>>>0;if(0===r)return"";for(var n="",i=0;;){if(n+=e[i],i+1===r)return n;n+=arguments[++i]}}function u(){var t,e,r=[],n=Math.floor,i=-1,o=arguments.length;if(!o)return"";for(;++iu||u>1114111||n(u)!=u)throw RangeError("Invalid code point: "+u);65535>=u?r.push(u):(u-=65536,t=(u>>10)+55296,e=u%1024+56320,r.push(t,e))}return String.fromCharCode.apply(null,r)}var s=Object.prototype.toString,a=String.prototype.indexOf,c=String.prototype.lastIndexOf;return{get startsWith(){return t},get endsWith(){return e},get contains(){return r},get repeat(){return n},get codePointAt(){return i},get raw(){return o},get fromCodePoint(){return u}}}),System.register("traceur-runtime@0.0.42/src/runtime/polyfills/polyfills",[],function(){"use strict";function t(t,e,r){e in t||Object.defineProperty(t,e,{value:r,configurable:!0,enumerable:!1,writable:!0})}function e(e,r){for(var n=0;n0)){var n=r.shift();n()}},!0),function(t){r.push(t),window.postMessage("process-tick","*")}}return function(t){setTimeout(t,0)}}(),n.title="browser",n.browser=!0,n.env={},n.argv=[],n.on=r,n.once=r,n.off=r,n.emit=r,n.binding=function(){throw new Error("process.binding is not supported")},n.cwd=function(){return"/"},n.chdir=function(){throw new Error("process.chdir is not supported")}},{}],3:[function(t,e,r){"use strict";Object.defineProperties(r,{"default":{get:function(){return a}},__esModule:{value:!0}});var n=$traceurRuntime.assertObject(t("./gif.js")).default,i=$traceurRuntime.assertObject(t("./stream_reader.js")).default,o=$traceurRuntime.assertObject(t("./utils.js")).Promises,u=URL&&URL.createObjectURL?URL:webkitURL,s=new Map,a=function(){var t=function(t){this.file=t};return $traceurRuntime.createClass(t,{load:function(){var t=this,e=s.get(this.file);if(e)return e;var r=o.xhrGet(this.file,"*/*","arraybuffer").then(function(e){return t.explode(e)});return s.set(this.file,r),r},explode:function(t){return console.debug("EXPLODING "+this.file),new Promise(function(e,r){var o=[],s=new i(t);if("GIF89a"!=s.readAscii(6))return void r(Error("Not a GIF!"));if(s.skipBytes(4),s.peekBit(1)){s.log("GLOBAL COLOR TABLE");var a=7&s.readByte();s.log("GLOBAL COLOR TABLE IS "+3*Math.pow(2,a+1)+" BYTES"),s.skipBytes(2),s.skipBytes(3*Math.pow(2,a+1))}else s.log("NO GLOBAL COLOR TABLE");for(var c=t.slice(0,s.index),l=!0,f=!1;l;)if(s.isNext([33,255])){s.log("APPLICATION EXTENSION"),s.skipBytes(2);var h=s.readByte();if(s.log(s.readAscii(h)),s.isNext([3,1]))s.skipBytes(5);else{for(s.log("A weird application extension. Skip until we have 2 NULL bytes");0!==s.readByte()||0!==s.peekByte(););s.log("OK moving on"),s.skipBytes(1)}}else if(s.isNext([33,254])){for(s.log("COMMENT EXTENSION"),s.skipBytes(2);!s.isNext([0]);){var h=s.readByte();s.log(s.readAscii(h))}s.skipBytes(1)}else if(s.isNext([44])){if(s.log("IMAGE DESCRIPTOR!"),f||o.push({index:s.index,delay:0}),f=!1,s.skipBytes(9),s.peekBit(1)){s.log("LOCAL COLOR TABLE");var a=7&s.readByte();s.log("LOCAL COLOR TABLE IS "+3*Math.pow(2,a+1)+" BYTES"),s.skipBytes(3*Math.pow(2,a+1))}else s.log("NO LOCAL TABLE PHEW"),s.skipBytes(1);for(s.log("MIN CODE SIZE "+s.readByte()),s.log("DATA START");!s.isNext([0]);){var h=s.readByte();s.skipBytes(h)}s.log("DATA END"),s.skipBytes(1)}else if(s.isNext([33,249,4])){s.log("GRAPHICS CONTROL EXTENSION!");var p=s.index;s.skipBytes(3);var m=s.readByte()>>2;s.log("DISPOSAL "+m);var d=s.readByte()+256*s.readByte();o.push({index:p,delay:d,disposal:m}),s.log("FRAME DELAY "+d),s.skipBytes(2),f=!0}else{for(var v=s.index;!s.finished()&&!s.isNext([33,249,4]);)s.readByte();s.finished()?(s.index=v,s.log("WE END"),l=!1):s.log("UNKNOWN DATA FROM "+v)}for(var g=s.index,y=t.slice(-1),b=0;b
',link:function(t,n,i){var o=Object.create(i,{fire:{value:function(t){console.log(t)}}});null!=o.exploded?o.playbackStrategy="noop":null!=o.sync?o.playbackStrategy="noop":o.hardBpm?o.playbackStrategy="hardBpm":o.bpm?o.playbackStrategy="bpm":(o.speed=o.speed||1,o.playbackStrategy="speed"),i.$observe("src",function(t){if(t){var i=r[o.playbackStrategy];o.playback=new e(o,n[0].querySelector(".x-gif__frames"),o.src,{pingPong:null!=o.pingPong,fill:null!=o.fill,stopped:null!=o.stopped}),o.playback.ready.then(i.bind(o))}}),i.$observe("speed",function(t){t&&o.playback&&(o.playback.speed=t)}),n[0].clock=function(t,e,r){o.playback&&o.playback.gif&&o.playback.fromClock(t,e,r)},n[0].relayout=function(){o.playback&&null!=o.fill&&o.playback.scaleToFill()}}}})},{"./playback.js":6,"./strategies.js":7}],5:[function(t,e,r){"use strict";Object.defineProperties(r,{"default":{get:function(){return i}},__esModule:{value:!0}});var n=10,i=function(){var t=function(t){var e=this;this.frames=t,this.length=0,this.offsets=[],t.forEach(function(t){e.offsets.push(e.length),e.length+=t.delay||n})};return $traceurRuntime.createClass(t,{frameAt:function(t){for(var e=t*this.length,r=1,n=this.offsets.length;n>r&&!(this.offsets[r]>e);r++);return r-1}},{})}()},{}],6:[function(t,e,r){"use strict";function n(t,e){t.classList.add("frame"),2==e.disposal&&t.classList.add("disposal-restore")}Object.defineProperties(r,{"default":{get:function(){return u}},__esModule:{value:!0}});var i=$traceurRuntime.assertObject(t("./exploder.js")).default,o=function(t){var e=new Image;return e.src=t.url,n(e,t),e},u=function(){var t=function(t,e,r,n){var u=this;this.xgif=t,this.element=e,this.onReady=n.onReady,this.pingPong=n.pingPong,this.fill=n.fill,this.stopped=n.stopped,this.snap=n.snap,this.nTimes=n.nTimes,this.ready=new Promise(function(t){var e=new i(r);e.load().then(function(e){console.debug("Received "+e.frames.length+" frames of gif "+r),u.gif=e,u.element.innerHTML="";var n=o;e.frames.map(n).forEach(u.element.appendChild,u.element),u.fill&&requestAnimationFrame(u.scaleToFill.bind(u)),t()})})};return $traceurRuntime.createClass(t,{scaleToFill:function(){if(this.element.offsetWidth&&this.element.offsetHeight){var t=this.element.parentElement.offsetWidth/this.element.offsetWidth,e=this.element.parentElement.offsetHeight/this.element.offsetHeight;this.element.style.webkitTransform="scale("+1.1*Math.max(t,e)+")"}else requestAnimationFrame(this.scaleToFill.bind(this))},setFrame:function(t,e){var r=this.gif.frameAt(this.pingPong&&e%2>=1?1-t:t);this.element.dataset.frame=r},start:function(){this.stopped=!1,this.startTime=performance.now(),this.animationLoop&&this.animationLoop()},stop:function(){this.stopped=!0},startSpeed:function(t){var e=this; 2 | this.speed=t,this.animationLoop=function(){var t=10*e.gif.length/e.speed,r=performance.now()-e.startTime,n=r/t,i=n%1;e.nTimes&&n>=e.nTimes?(e.setFrame(e.nTimes%1||1,n),e.element.dispatchEvent(new CustomEvent("x-gif-finished"),!0)):(e.setFrame(i,n),e.stopped||requestAnimationFrame(e.animationLoop))},this.stopped||this.start()},fromClock:function(t,e,r){var n=1.5,i=this.snap?1:Math.max(1,Math.round(1/n*10*this.gif.length/e)),o=t%i,u=t/i,s=r/i+o/i;this.setFrame(s,u)},changeBpm:function(t){this.beatLength=6e4/t},startBpm:function(t){var e=this;this.changeBpm(t),this.animationLoop=function(){var t=performance.now()-e.startTime,r=Math.floor(t/e.beatLength),n=t%e.beatLength/e.beatLength;e.fromClock(r,e.beatLength,n),e.stopped||requestAnimationFrame(e.animationLoop)},this.stopped||this.start()}},{})}()},{"./exploder.js":3}],7:[function(t,e,r){"use strict";Object.defineProperties(r,{"default":{get:function(){return i}},__esModule:{value:!0}});var n={speed:function(){this.playback.startSpeed(this.speed,this.context.getAttribute("n-times"))},hardBpm:function(){this.playback.startHardBpm(parseFloat(this.context.getAttribute("hard-bpm")))},bpm:function(){this.playback.startBpm(parseFloat(this.context.getAttribute("bpm")))},noop:function(){}},i=n},{}],8:[function(t,e,r){"use strict";Object.defineProperties(r,{"default":{get:function(){return n}},__esModule:{value:!0}});var n=function(){var t=function(t){this.data=new Uint8Array(t),this.index=0,this.log("TOTAL LENGTH: "+this.data.length)};return $traceurRuntime.createClass(t,{finished:function(){return this.index>=this.data.length},readByte:function(){return this.data[this.index++]},peekByte:function(){return this.data[this.index]},skipBytes:function(t){this.index+=t},peekBit:function(t){return!!(this.peekByte()&1<<8-t)},readAscii:function(t){for(var e="",r=0;t>r;r++)e+=String.fromCharCode(this.readByte());return e},isNext:function(t){for(var e=0;e3?("function"==typeof n&&(t.__proto__=n),t.prototype=l(s(n),i(e))):t.prototype=e,h(t,"prototype",{configurable:!1,writable:!1}),f(t,i(r))}function s(t){if("function"==typeof t){var e=t.prototype;if(a(e)===e||null===e)return t.prototype}if(null===t)return null;throw new c}function u(t,r,n){null!==m(r)&&e(t,r,"constructor",n)}var a=Object,c=TypeError,l=a.create,f=$traceurRuntime.defineProperties,h=$traceurRuntime.defineProperty,p=$traceurRuntime.getOwnPropertyDescriptor,d=$traceurRuntime.getOwnPropertyNames,m=Object.getPrototypeOf;$traceurRuntime.createClass=o,$traceurRuntime.defaultSuperCall=u,$traceurRuntime.superCall=e,$traceurRuntime.superGet=r,$traceurRuntime.superSet=n}(),function(){"use strict";function t(t){return{configurable:!0,enumerable:!1,value:t,writable:!0}}function e(t){return new Error("Traceur compiler bug: invalid state in state machine: "+t)}function r(){this.state=0,this.GState=v,this.storedException=void 0,this.finallyFallThrough=void 0,this.sent_=void 0,this.returnValue=void 0,this.tryStack_=[]}function n(t,e,r,n){switch(t.GState){case b:throw new Error('"'+r+'" on executing generator');case j:if("next"==r)return{value:void 0,done:!0};throw new Error('"'+r+'" on closed generator');case v:if("throw"===r)throw t.GState=j,n;if(void 0!==n)throw g("Sent value to newborn generator");case y:t.GState=b,t.action=r,t.sent=n;var i=e(t),o=i===t;return o&&(i=t.returnValue),t.GState=o?j:y,{value:i,done:o}}}function i(){}function o(){}function s(t,e,n){var i=l(t,n),o=new r,s=m(e.prototype);return s[x]=o,s[S]=i,s}function u(t){return t.prototype=m(o.prototype),t.__proto__=o,t}function a(){r.call(this),this.err=void 0;var t=this;t.result=new Promise(function(e,r){t.resolve=e,t.reject=r})}function c(t,e){var r=l(t,e),n=new a;return n.createCallback=function(t){return function(e){n.state=t,n.value=e,r(n)}},n.errback=function(t){f(n,t),r(n)},r(n),n.result}function l(t,e){return function(r){for(;;)try{return t.call(e,r)}catch(n){f(r,n)}}}function f(t,e){t.storedException=e;var r=t.tryStack_[t.tryStack_.length-1];return r?(t.state=void 0!==r.catch?r.catch:r.finally,void(void 0!==r.finallyFallThrough&&(t.finallyFallThrough=r.finallyFallThrough))):void t.handleException(e)}var h=$traceurRuntime.createPrivateName,p=$traceurRuntime.defineProperties,d=$traceurRuntime.defineProperty,m=Object.create,g=TypeError,v=0,b=1,y=2,j=3,w=-2,O=-3;r.prototype={pushTry:function(t,e){if(null!==e){for(var r=null,n=this.tryStack_.length-1;n>=0;n--)if(void 0!==this.tryStack_[n].catch){r=this.tryStack_[n].catch;break}null===r&&(r=O),this.tryStack_.push({"finally":e,finallyFallThrough:r})}null!==t&&this.tryStack_.push({"catch":t})},popTry:function(){this.tryStack_.pop()},get sent(){return this.maybeThrow(),this.sent_},set sent(t){this.sent_=t},get sentIgnoreThrow(){return this.sent_},maybeThrow:function(){if("throw"===this.action)throw this.action="next",this.sent_},end:function(){switch(this.state){case w:return this;case O:throw this.storedException;default:throw e(this.state)}},handleException:function(t){throw this.GState=j,this.state=w,t}};var x=h(),S=h();i.prototype=o,d(o,"constructor",t(i)),o.prototype={constructor:o,next:function(t){return n(this[x],this[S],"next",t)},"throw":function(t){return n(this[x],this[S],"throw",t)}},p(o.prototype,{constructor:{enumerable:!1},next:{enumerable:!1},"throw":{enumerable:!1}}),Object.defineProperty(o.prototype,Symbol.iterator,t(function(){return this})),a.prototype=m(r.prototype),a.prototype.end=function(){switch(this.state){case w:this.resolve(this.returnValue);break;case O:this.reject(this.storedException);break;default:this.reject(e(this.state))}},a.prototype.handleException=function(){this.state=O},$traceurRuntime.asyncWrap=c,$traceurRuntime.initGeneratorFunction=u,$traceurRuntime.createGeneratorInstance=s}(),function(){function t(t,e,r,n,i,o,s){var u=[];return t&&u.push(t,":"),r&&(u.push("//"),e&&u.push(e,"@"),u.push(r),n&&u.push(":",n)),i&&u.push(i),o&&u.push("?",o),s&&u.push("#",s),u.join("")}function e(t){return t.match(u)}function r(t){if("/"===t)return"/";for(var e="/"===t[0]?"/":"",r="/"===t.slice(-1)?"/":"",n=t.split("/"),i=[],o=0,s=0;s0;)i.unshift("..");0===i.length&&i.push(".")}return e+i.join("/")+r}function n(e){var n=e[a.PATH]||"";return n=r(n),e[a.PATH]=n,t(e[a.SCHEME],e[a.USER_INFO],e[a.DOMAIN],e[a.PORT],e[a.PATH],e[a.QUERY_DATA],e[a.FRAGMENT])}function i(t){var r=e(t);return n(r)}function o(t,r){var i=e(r),o=e(t);if(i[a.SCHEME])return n(i);i[a.SCHEME]=o[a.SCHEME];for(var s=a.SCHEME;s<=a.PORT;s++)i[s]||(i[s]=o[s]);if("/"==i[a.PATH][0])return n(i);var u=o[a.PATH],c=u.lastIndexOf("/");return u=u.slice(0,c+1)+i[a.PATH],i[a.PATH]=u,n(i)}function s(t){if(!t)return!1;if("/"===t[0])return!0;var r=e(t);return r[a.SCHEME]?!0:!1}var u=new RegExp("^(?:([^:/?#.]+):)?(?://(?:([^/?#]*)@)?([\\w\\d\\-\\u0100-\\uffff.%]*)(?::([0-9]+))?)?([^?#]+)?(?:\\?([^#]*))?(?:#(.*))?$"),a={SCHEME:1,USER_INFO:2,DOMAIN:3,PORT:4,PATH:5,QUERY_DATA:6,FRAGMENT:7};$traceurRuntime.canonicalizeUrl=i,$traceurRuntime.isAbsolute=s,$traceurRuntime.removeDotSegments=r,$traceurRuntime.resolveUrl=o}(),function(t){"use strict";function e(t){if(t){var e=d.normalize(t);return a[e]}}function r(t){var e=arguments[1],r=Object.create(null);return Object.getOwnPropertyNames(t).forEach(function(n){var i,o;if(e===p){var s=Object.getOwnPropertyDescriptor(t,n);s.get&&(i=s.get)}i||(o=t[n],i=function(){return o}),Object.defineProperty(r,n,{get:i,enumerable:!0})}),Object.preventExtensions(r),r}var n,i=$traceurRuntime.assertObject($traceurRuntime),o=i.canonicalizeUrl,s=i.resolveUrl,u=i.isAbsolute,a=Object.create(null);n=t.location&&t.location.href?s(t.location.href,"./"):"";var c=function(t,e){this.url=t,this.value_=e};$traceurRuntime.createClass(c,{},{});var l=function(t,e){$traceurRuntime.superCall(this,f.prototype,"constructor",[t,null]),this.func=e},f=l;$traceurRuntime.createClass(l,{getUncoatedModule:function(){return this.value_?this.value_:this.value_=this.func.call(t)}},{},c);var h=Object.create(null),p={},d={normalize:function(t,e){if("string"!=typeof t)throw new TypeError("module name must be a string, not "+typeof t);if(u(t))return o(t);if(/[^\.]\/\.\.\//.test(t))throw new Error("module name embeds /../: "+t);return"."===t[0]&&e?s(e,t):o(t)},get:function(t){var n=e(t);if(!n)return void 0;var i=h[n.url];return i?i:(i=r(n.getUncoatedModule(),p),h[n.url]=i)},set:function(t,e){t=String(t),a[t]=new l(t,function(){return e}),h[t]=e},get baseURL(){return n},set baseURL(t){n=String(t)},registerModule:function(t,e){var r=d.normalize(t);if(a[r])throw new Error("duplicate module named "+r);a[r]=new l(r,e)},bundleStore:Object.create(null),register:function(t,e,r){e&&(e.length||r.length)?this.bundleStore[t]={deps:e,execute:function(){var t=arguments,n={};e.forEach(function(e,r){return n[e]=t[r]});var i=r.call(this,n);return i.execute.call(this),i.exports}}:this.registerModule(t,r)},getAnonymousModule:function(e){return new r(e.call(t),p)},getForTesting:function(t){var e=this;return this.testingPrefix_||Object.keys(h).some(function(t){var r=/(traceur@[^\/]*\/)/.exec(t);return r?(e.testingPrefix_=r[1],!0):void 0}),this.get(this.testingPrefix_+t)}};d.set("@traceur/src/runtime/ModuleStore",new r({ModuleStore:d}));var m=$traceurRuntime.setupGlobals;$traceurRuntime.setupGlobals=function(t){m(t)},$traceurRuntime.ModuleStore=d,t.System={register:d.register.bind(d),get:d.get,set:d.set,normalize:d.normalize},$traceurRuntime.getModuleImpl=function(t){var r=e(t);return r&&r.getUncoatedModule()}}("undefined"!=typeof e?e:this),System.register("traceur-runtime@0.0.42/src/runtime/polyfills/utils",[],function(){"use strict";function t(t){return 0|t}function e(t){return t&&("object"==typeof t||"function"==typeof t)}function r(t){return"function"==typeof t}function n(t){return t=+t,isNaN(t)?0:isFinite(t)&&0!==t?t>0?Math.floor(t):Math.ceil(t):t}function i(t){var e=n(t);return 0>e?0:Math.min(e,s)}var o=$traceurRuntime.toObject,s=Math.pow(2,53)-1;return{get toObject(){return o},get toUint32(){return t},get isObject(){return e},get isCallable(){return r},get toInteger(){return n},get toLength(){return i}}}),System.register("traceur-runtime@0.0.42/src/runtime/polyfills/Array",[],function(){"use strict";function t(t){var e=void 0!==arguments[1]?arguments[1]:0,r=arguments[2],n=u(this),i=s(n.length),a=o(e),c=void 0!==r?o(r):i;for(a=0>a?Math.max(i+a,0):Math.min(a,i),c=0>c?Math.max(i+c,0):Math.min(c,i);c>a;)n[a]=t,a++;return n}function e(t){var e=arguments[1];return n(this,t,e)}function r(t){var e=arguments[1];return n(this,t,e,!0)}function n(t,e){var r=arguments[2],n=void 0!==arguments[3]?arguments[3]:!1,i=u(t),o=s(i.length);if(!a(e))throw TypeError();for(var c=0;o>c;c++)if(c in i){var l=i[c];if(e.call(r,l,c,i))return n?c:l}return n?-1:void 0}var i=$traceurRuntime.assertObject(System.get("traceur-runtime@0.0.42/src/runtime/polyfills/utils")),o=i.toInteger,s=i.toLength,u=i.toObject,a=i.isCallable;return{get fill(){return t},get find(){return e},get findIndex(){return r}}}),System.register("traceur-runtime@0.0.42/src/runtime/polyfills/ArrayIterator",[],function(){"use strict";function t(t,e){var r=u(t),n=new h;return n.iteratorObject_=r,n.arrayIteratorNextIndex_=0,n.arrayIterationKind_=e,n}function e(t,e){return{value:t,done:e}}function r(){return t(this,f)}function n(){return t(this,c)}function i(){return t(this,l)}var o,s=$traceurRuntime.assertObject(System.get("traceur-runtime@0.0.42/src/runtime/polyfills/utils")),u=s.toObject,a=s.toUint32,c=1,l=2,f=3,h=function(){};return $traceurRuntime.createClass(h,(o={},Object.defineProperty(o,"next",{value:function(){var t=u(this),r=t.iteratorObject_;if(!r)throw new TypeError("Object is not an ArrayIterator");var n=t.arrayIteratorNextIndex_,i=t.arrayIterationKind_,o=a(r.length);return n>=o?(t.arrayIteratorNextIndex_=1/0,e(void 0,!0)):(t.arrayIteratorNextIndex_=n+1,i==l?e(r[n],!1):i==f?e([n,r[n]],!1):e(n,!1))},configurable:!0,enumerable:!0,writable:!0}),Object.defineProperty(o,Symbol.iterator,{value:function(){return this},configurable:!0,enumerable:!0,writable:!0}),o),{}),{get entries(){return r},get keys(){return n},get values(){return i}}}),System.register("traceur-runtime@0.0.42/src/runtime/polyfills/Map",[],function(){"use strict";function t(t,e){if(r(e)){var i=n(e);return i&&t.objectIndex_[i.hash]}return"string"==typeof e?t.stringIndex_[e]:t.primitiveIndex_[e]}function e(t){t.entries_=[],t.objectIndex_=Object.create(null),t.stringIndex_=Object.create(null),t.primitiveIndex_=Object.create(null),t.deletedCount_=0}var r=$traceurRuntime.assertObject(System.get("traceur-runtime@0.0.42/src/runtime/polyfills/utils")).isObject,n=$traceurRuntime.getOwnHashObject,i=Object.prototype.hasOwnProperty,o={},s=function(){var t=arguments[0];if(!r(this))throw new TypeError("Constructor Map requires 'new'");if(i.call(this,"entries_"))throw new TypeError("Map can not be reentrantly initialised");if(e(this),null!==t&&void 0!==t){var n=t[Symbol.iterator];if(void 0!==n)for(var o,s=t[Symbol.iterator]();!(o=s.next()).done;){var u=$traceurRuntime.assertObject(o.value),a=u[0],c=u[1];this.set(a,c)}}};return $traceurRuntime.createClass(s,{get size(){return this.entries_.length/2-this.deletedCount_},get:function(e){var r=t(this,e);return void 0!==r?this.entries_[r+1]:void 0},set:function(e,i){var o=r(e),s="string"==typeof e,u=t(this,e);if(void 0!==u)this.entries_[u+1]=i;else if(u=this.entries_.length,this.entries_[u]=e,this.entries_[u+1]=i,o){var a=n(e),c=a.hash;this.objectIndex_[c]=u}else s?this.stringIndex_[e]=u:this.primitiveIndex_[e]=u;return this},has:function(e){return void 0!==t(this,e)},"delete":function(t){var e,i,s=r(t),u="string"==typeof t;if(s){var a=n(t);a&&(e=this.objectIndex_[i=a.hash],delete this.objectIndex_[i])}else u?(e=this.stringIndex_[t],delete this.stringIndex_[t]):(e=this.primitiveIndex_[t],delete this.primitiveIndex_[t]);void 0!==e&&(this.entries_[e]=o,this.entries_[e+1]=void 0,this.deletedCount_++)},clear:function(){e(this)},forEach:function(t){for(var e=arguments[1],r=0,n=this.entries_.length;n>r;r+=2){var i=this.entries_[r],s=this.entries_[r+1];i!==o&&t.call(e,s,i,this)}}},{}),{get Map(){return s}}}),System.register("traceur-runtime@0.0.42/src/runtime/polyfills/Object",[],function(){"use strict";function t(t,e){return t===e?0!==t||1/t===1/e:t!==t&&e!==e}function e(t){for(var e=1;er;r++){var s=i[r];a[s]||(t[s]=n[s])}}return t}function r(t,e){var r,n,u=s(e),c=u.length;for(r=0;c>r;r++){var l=u[r];a[l]||(n=o(e,u[r]),i(t,u[r],n))}return t}var n=$traceurRuntime.assertObject(System.get("traceur-runtime@0.0.42/src/runtime/polyfills/utils")),n=(n.toInteger,n.toLength,n.toObject,n.isCallable,$traceurRuntime.assertObject($traceurRuntime)),i=n.defineProperty,o=n.getOwnPropertyDescriptor,s=n.getOwnPropertyNames,u=n.keys,a=n.privateNames;return{get is(){return t},get assign(){return e},get mixin(){return r}}}),System.register("traceur-runtime@0.0.42/node_modules/rsvp/lib/rsvp/asap",[],function(){"use strict";function e(){return function(){t.nextTick(i)}}function r(){var t=0,e=new a(i),r=document.createTextNode("");return e.observe(r,{characterData:!0}),function(){r.data=t=++t%2}}function n(){return function(){setTimeout(i,1)}}function i(){for(var t=0;t1?arguments[1]:void 0),o=i?Number(i):0;isNaN(o)&&(o=0);var s=Math.min(Math.max(o,0),r);return a.call(e,n,o)==s}function e(t){var e=String(this);if(null==this||"[object RegExp]"==u.call(t))throw TypeError();var r=e.length,n=String(t),i=n.length,o=r;if(arguments.length>1){var s=arguments[1];void 0!==s&&(o=s?Number(s):0,isNaN(o)&&(o=0))}var a=Math.min(Math.max(o,0),r),l=a-i;return 0>l?!1:c.call(e,n,l)==l}function r(t){if(null==this)throw TypeError();var e=String(this),r=e.length,n=String(t),i=(n.length,arguments.length>1?arguments[1]:void 0),o=i?Number(i):0;isNaN(o)&&(o=0);Math.min(Math.max(o,0),r);return-1!=a.call(e,n,o)}function n(t){if(null==this)throw TypeError();var e=String(this),r=t?Number(t):0;if(isNaN(r)&&(r=0),0>r||1/0==r)throw RangeError();if(0==r)return"";for(var n="";r--;)n+=e;return n}function i(t){if(null==this)throw TypeError();var e=String(this),r=e.length,n=t?Number(t):0;if(isNaN(n)&&(n=0),0>n||n>=r)return void 0;var i,o=e.charCodeAt(n);return o>=55296&&56319>=o&&r>n+1&&(i=e.charCodeAt(n+1),i>=56320&&57343>=i)?1024*(o-55296)+i-56320+65536:o}function o(t){var e=t.raw,r=e.length>>>0;if(0===r)return"";for(var n="",i=0;;){if(n+=e[i],i+1===r)return n;n+=arguments[++i]}}function s(){var t,e,r=[],n=Math.floor,i=-1,o=arguments.length;if(!o)return"";for(;++is||s>1114111||n(s)!=s)throw RangeError("Invalid code point: "+s);65535>=s?r.push(s):(s-=65536,t=(s>>10)+55296,e=s%1024+56320,r.push(t,e))}return String.fromCharCode.apply(null,r)}var u=Object.prototype.toString,a=String.prototype.indexOf,c=String.prototype.lastIndexOf;return{get startsWith(){return t},get endsWith(){return e},get contains(){return r},get repeat(){return n},get codePointAt(){return i},get raw(){return o},get fromCodePoint(){return s}}}),System.register("traceur-runtime@0.0.42/src/runtime/polyfills/polyfills",[],function(){"use strict";function t(t,e,r){e in t||Object.defineProperty(t,e,{value:r,configurable:!0,enumerable:!1,writable:!0})}function e(e,r){for(var n=0;n0)){var n=r.shift();n()}},!0),function(t){r.push(t),window.postMessage("process-tick","*")}}return function(t){setTimeout(t,0)}}(),n.title="browser",n.browser=!0,n.env={},n.argv=[],n.on=r,n.once=r,n.off=r,n.emit=r,n.binding=function(){throw new Error("process.binding is not supported")},n.cwd=function(){return"/"},n.chdir=function(){throw new Error("process.chdir is not supported")}},{}],3:[function(t,e,r){"use strict";Object.defineProperties(r,{"default":{get:function(){return a}},__esModule:{value:!0}});var n=$traceurRuntime.assertObject(t("./gif.js")).default,i=$traceurRuntime.assertObject(t("./stream_reader.js")).default,o=$traceurRuntime.assertObject(t("./utils.js")).Promises,s=URL&&URL.createObjectURL?URL:webkitURL,u=new Map,a=function(){var t=function(t){this.file=t};return $traceurRuntime.createClass(t,{load:function(){var t=this,e=u.get(this.file);if(e)return e;var r=o.xhrGet(this.file,"*/*","arraybuffer").then(function(e){return t.explode(e)});return u.set(this.file,r),r},explode:function(t){return console.debug("EXPLODING "+this.file),new Promise(function(e,r){var o=[],u=new i(t);if("GIF89a"!=u.readAscii(6))return void r(Error("Not a GIF!"));if(u.skipBytes(4),u.peekBit(1)){u.log("GLOBAL COLOR TABLE");var a=7&u.readByte();u.log("GLOBAL COLOR TABLE IS "+3*Math.pow(2,a+1)+" BYTES"),u.skipBytes(2),u.skipBytes(3*Math.pow(2,a+1))}else u.log("NO GLOBAL COLOR TABLE");for(var c=t.slice(0,u.index),l=!0,f=!1;l;)if(u.isNext([33,255])){u.log("APPLICATION EXTENSION"),u.skipBytes(2);var h=u.readByte();if(u.log(u.readAscii(h)),u.isNext([3,1]))u.skipBytes(5);else{for(u.log("A weird application extension. Skip until we have 2 NULL bytes");0!==u.readByte()||0!==u.peekByte(););u.log("OK moving on"),u.skipBytes(1)}}else if(u.isNext([33,254])){for(u.log("COMMENT EXTENSION"),u.skipBytes(2);!u.isNext([0]);){var h=u.readByte();u.log(u.readAscii(h))}u.skipBytes(1)}else if(u.isNext([44])){if(u.log("IMAGE DESCRIPTOR!"),f||o.push({index:u.index,delay:0}),f=!1,u.skipBytes(9),u.peekBit(1)){u.log("LOCAL COLOR TABLE");var a=7&u.readByte();u.log("LOCAL COLOR TABLE IS "+3*Math.pow(2,a+1)+" BYTES"),u.skipBytes(3*Math.pow(2,a+1))}else u.log("NO LOCAL TABLE PHEW"),u.skipBytes(1);for(u.log("MIN CODE SIZE "+u.readByte()),u.log("DATA START");!u.isNext([0]);){var h=u.readByte();u.skipBytes(h)}u.log("DATA END"),u.skipBytes(1)}else if(u.isNext([33,249,4])){u.log("GRAPHICS CONTROL EXTENSION!");var p=u.index;u.skipBytes(3);var d=u.readByte()>>2;u.log("DISPOSAL "+d);var m=u.readByte()+256*u.readByte();o.push({index:p,delay:m,disposal:d}),u.log("FRAME DELAY "+m),u.skipBytes(2),f=!0}else{for(var g=u.index;!u.finished()&&!u.isNext([33,249,4]);)u.readByte();u.finished()?(u.index=g,u.log("WE END"),l=!1):u.log("UNKNOWN DATA FROM "+g)}for(var v=u.index,b=t.slice(-1),y=0;yr&&!(this.offsets[r]>e);r++);return r-1}},{})}()},{}],6:[function(t,e,r){"use strict";function n(t,e){t.classList.add("frame"),2==e.disposal&&t.classList.add("disposal-restore")}Object.defineProperties(r,{"default":{get:function(){return s}},__esModule:{value:!0}});var i=$traceurRuntime.assertObject(t("./exploder.js")).default,o=function(t){var e=new Image;return e.src=t.url,n(e,t),e},s=function(){var t=function(t,e,r,n){var s=this;this.xgif=t,this.element=e,this.onReady=n.onReady,this.pingPong=n.pingPong,this.fill=n.fill,this.stopped=n.stopped,this.snap=n.snap,this.nTimes=n.nTimes,this.ready=new Promise(function(t){var e=new i(r);e.load().then(function(e){console.debug("Received "+e.frames.length+" frames of gif "+r),s.gif=e,s.element.innerHTML="";var n=o;e.frames.map(n).forEach(s.element.appendChild,s.element),s.fill&&requestAnimationFrame(s.scaleToFill.bind(s)),t()})})};return $traceurRuntime.createClass(t,{scaleToFill:function(){if(this.element.offsetWidth&&this.element.offsetHeight){var t=this.element.parentElement.offsetWidth/this.element.offsetWidth,e=this.element.parentElement.offsetHeight/this.element.offsetHeight;this.element.style.webkitTransform="scale("+1.1*Math.max(t,e)+")"}else requestAnimationFrame(this.scaleToFill.bind(this))},setFrame:function(t,e){var r=this.gif.frameAt(this.pingPong&&e%2>=1?1-t:t);this.element.dataset.frame=r},start:function(){this.stopped=!1,this.startTime=performance.now(),this.animationLoop&&this.animationLoop()},stop:function(){this.stopped=!0},startSpeed:function(t){var e=this;this.speed=t,this.animationLoop=function(){var t=10*e.gif.length/e.speed,r=performance.now()-e.startTime,n=r/t,i=n%1;e.nTimes&&n>=e.nTimes?(e.setFrame(e.nTimes%1||1,n),e.element.dispatchEvent(new CustomEvent("x-gif-finished"),!0)):(e.setFrame(i,n),e.stopped||requestAnimationFrame(e.animationLoop))},this.stopped||this.start()},fromClock:function(t,e,r){var n=1.5,i=this.snap?1:Math.max(1,Math.round(1/n*10*this.gif.length/e)),o=t%i,s=t/i,u=r/i+o/i;this.setFrame(u,s)},changeBpm:function(t){this.beatLength=6e4/t},startBpm:function(t){var e=this;this.changeBpm(t),this.animationLoop=function(){var t=performance.now()-e.startTime,r=Math.floor(t/e.beatLength),n=t%e.beatLength/e.beatLength;e.fromClock(r,e.beatLength,n),e.stopped||requestAnimationFrame(e.animationLoop)},this.stopped||this.start()}},{})}()},{"./exploder.js":3}],7:[function(t,e,r){"use strict";Object.defineProperties(r,{"default":{get:function(){return i}},__esModule:{value:!0}});var n={speed:function(){this.playback.startSpeed(this.speed,this.context.getAttribute("n-times"))},hardBpm:function(){this.playback.startHardBpm(parseFloat(this.context.getAttribute("hard-bpm")))},bpm:function(){this.playback.startBpm(parseFloat(this.context.getAttribute("bpm")))},noop:function(){}},i=n},{}],8:[function(t,e,r){"use strict";Object.defineProperties(r,{"default":{get:function(){return n}},__esModule:{value:!0}});var n=function(){var t=function(t){this.data=new Uint8Array(t),this.index=0,this.log("TOTAL LENGTH: "+this.data.length)};return $traceurRuntime.createClass(t,{finished:function(){return this.index>=this.data.length},readByte:function(){return this.data[this.index++]},peekByte:function(){return this.data[this.index]},skipBytes:function(t){this.index+=t},peekBit:function(t){return!!(this.peekByte()&1<<8-t)},readAscii:function(t){for(var e="",r=0;t>r;r++)e+=String.fromCharCode(this.readByte());return e},isNext:function(t){for(var e=0;e 1%")) 30 | .pipe(gulp.dest('dist')); 31 | }) 32 | 33 | gulp.task('vulcanize', function () { 34 | gulp.src('dist/x-gif.local.html') 35 | .pipe($.vulcanize({dest: 'dist', inline: true})) 36 | .pipe($.rename('x-gif.html')) 37 | .pipe(gulp.dest('dist')); 38 | }) 39 | 40 | gulp.task('copy', function () { 41 | gulp.src([ 42 | 'bower_components/platform/platform.js', 43 | 'bower_components/polymer/polymer*', 44 | 'bower_components/polymer/layout*', 45 | ]) 46 | .pipe(gulp.dest('dist')); 47 | }); 48 | 49 | gulp.task('build', ['js', 'html', 'css', 'copy', 'vulcanize']); 50 | 51 | gulp.task('default', ['build', 'connect'], function () { 52 | gulp.watch(['src/*.*js'], ['js']); 53 | gulp.watch(['src/*.html'], ['html']); 54 | gulp.watch(['src/*.scss'], ['css']); 55 | gulp.watch(['bower_components'], ['copy']); 56 | gulp.watch(['dist/x-gif.local.html', 'dist/x-gif.js', 'dist/x-gif.css'], ['vulcanize']); 57 | 58 | gulp.watch(['index.html', 'dist/**.*', 'demos/**.*'], function (event) { 59 | return gulp.src(event.path) 60 | .pipe($.connect.reload()); 61 | }); 62 | }); 63 | 64 | gulp.task('connect', function () { 65 | $.connect.server({ 66 | root: [__dirname], 67 | port: 1983, 68 | livereload: {port: 2983} 69 | }) 70 | }); 71 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | <x-gif> 8 | 9 | 19 | 20 | 21 | 22 | 60 | 61 | 62 |
63 |
64 |
65 |

<x-gif>

66 |

The GIF tag the internet deserves

67 |
View source on GitHub
68 |
69 |
70 | 71 | 76 | 80 | 81 |

Normal playback

82 | 83 |

Something not look right? Post an 84 | issue on GitHub with the URL of the image

85 |
86 | 87 |
88 |
<x-gif src="{{ trustedUrl() }}"></x-gif>
89 |
90 |
91 | 92 |

Ping-pong

93 |
94 | 95 |
96 |
<x-gif src="{{ trustedUrl() }}" ping-pong></x-gif>
97 |
98 |
99 | 100 |

N-times

101 | 102 | {{ gif.nTimes }} 103 |
104 | 105 |
106 |
<x-gif src="{{ trustedUrl() }}" n-times="{{ gif.nTimes }}"{{ nTimesIsStopped() }}></x-gif>
107 |
108 |
109 | 110 |

Speed

111 |
112 | 115 | 116 |
117 |
<x-gif src="{{ trustedUrl() }}" speed="{{ gif.speed }}"></x-gif>
118 |
119 |
120 | 121 |

BPM

122 |
123 | 126 | 127 |
128 |
<x-gif src="{{ trustedUrl() }}" bpm="{{ gif.bpm }}"></x-gif>
129 |
130 | 131 |
132 |
<x-gif src="{{ trustedUrl() }}" bpm="{{ gif.bpm }}" snap></x-gif>
133 |
134 |
135 | 136 |

Synced to Audio

137 |
138 |
139 | 143 |

Playback speed: {{ gif.playbackRate | number:1 }} + - 145 |

146 |
147 |
Loading beat data...
148 | 149 |
150 |
<x-gif src="{{ trustedUrl() }}" synced></x-gif>
151 |
152 | 153 |
154 |
<x-gif src="{{ trustedUrl() }}" synced snap></x-gif>
155 |

156 | Audio: Encom Part II by Daft Punk. CC0 1.0 Universal. 157 |
158 | Source: 159 | Archive.org 160 |
161 | Beat metadata provided by Echo Nest 162 |

163 |
See a full screen demo
164 |
165 |
166 | 167 |

Stop-start

168 |
<x-gif src="{{ trustedUrl() }}" stopped></x-gif>
169 |
See demo
170 | 171 |

Exploded

172 |
<x-gif src="{{ trustedUrl() }}" exploded></x-gif>
173 |

Explode the gif into its component frames (handy for debugging)

174 |
See demo
175 | 176 |
177 | 178 |
179 |
Made with <3 by
180 | 181 |
182 | @glenmaddern 183 |
184 |
185 |
186 | 187 | 240 | 257 | 258 | 259 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "x-gif", 3 | "version": "0.0.0", 4 | "description": "The GIF element the internet deserves", 5 | "main": "dist/x-gif.html", 6 | "readme": "README.md", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git://github.com/geelen/x-gif.git" 13 | }, 14 | "author": "Glen Maddern", 15 | "license": "MIT", 16 | "bugs": { 17 | "url": "https://github.com/geelen/x-gif/issues" 18 | }, 19 | "devDependencies": { 20 | "gulp": "~3.6.0", 21 | "gulp-sass": "~0.7.1", 22 | "gulp-concat": "~2.2.0", 23 | "gulp-autoprefixer": "0.0.6", 24 | "gulp-load-plugins": "~0.5.0", 25 | "gulp-plumber": "~0.5.6", 26 | "gulp-browserify": "~0.5.0", 27 | "gulp-ruby-sass": "~0.4.1", 28 | "gulp-connect": "~2.0.4", 29 | "es6ify": "~1.2.0", 30 | "gulp-uglify": "~0.3.1", 31 | "gulp-rename": "~1.2.0", 32 | "gulp-vulcanize": "~0.2.1" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/exploder.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import Gif from './gif.js'; 4 | import StreamReader from './stream_reader.js'; 5 | import { Promises } from './utils.js'; 6 | 7 | var url = (URL && URL.createObjectURL) ? URL : webkitURL; 8 | 9 | var gifCache = new Map(); 10 | export default class Exploder { 11 | constructor(file) { 12 | this.file = file; 13 | } 14 | 15 | load() { 16 | var cachedGifPromise = gifCache.get(this.file) 17 | if (cachedGifPromise) return cachedGifPromise; 18 | 19 | var gifPromise = Promises.xhrGet(this.file, '*/*', 'arraybuffer') 20 | .then(buffer => this.explode(buffer)); 21 | 22 | gifCache.set(this.file, gifPromise); 23 | return gifPromise; 24 | } 25 | 26 | explode(buffer) { 27 | console.debug("EXPLODING " + this.file) 28 | return new Promise((resolve, reject) => { 29 | var frames = [], 30 | streamReader = new StreamReader(buffer); 31 | 32 | // Ensure this is an animated GIF 33 | if (streamReader.readAscii(6) != "GIF89a") { 34 | reject(Error("Not a GIF!")); 35 | return; 36 | } 37 | 38 | streamReader.skipBytes(4); // Height & Width 39 | if (streamReader.peekBit(1)) { 40 | streamReader.log("GLOBAL COLOR TABLE") 41 | var colorTableSize = streamReader.readByte() & 0x07; 42 | streamReader.log("GLOBAL COLOR TABLE IS " + 3 * Math.pow(2, colorTableSize + 1) + " BYTES") 43 | streamReader.skipBytes(2); 44 | streamReader.skipBytes(3 * Math.pow(2, colorTableSize + 1)); 45 | } else { 46 | streamReader.log("NO GLOBAL COLOR TABLE") 47 | } 48 | // WE HAVE ENOUGH FOR THE GIF HEADER! 49 | var gifHeader = buffer.slice(0, streamReader.index); 50 | 51 | var spinning = true, expectingImage = false; 52 | while (spinning) { 53 | 54 | if (streamReader.isNext([0x21, 0xFF])) { 55 | streamReader.log("APPLICATION EXTENSION") 56 | streamReader.skipBytes(2); 57 | var blockSize = streamReader.readByte(); 58 | streamReader.log(streamReader.readAscii(blockSize)); 59 | 60 | if (streamReader.isNext([0x03, 0x01])) { 61 | // we cool 62 | streamReader.skipBytes(5) 63 | } else { 64 | streamReader.log("A weird application extension. Skip until we have 2 NULL bytes"); 65 | while (!(streamReader.readByte() === 0 && streamReader.peekByte() === 0)); 66 | streamReader.log("OK moving on") 67 | streamReader.skipBytes(1); 68 | } 69 | } else if (streamReader.isNext([0x21, 0xFE])) { 70 | streamReader.log("COMMENT EXTENSION") 71 | streamReader.skipBytes(2); 72 | 73 | while (!streamReader.isNext([0x00])) { 74 | var blockSize = streamReader.readByte(); 75 | streamReader.log(streamReader.readAscii(blockSize)); 76 | } 77 | streamReader.skipBytes(1); //NULL terminator 78 | 79 | } else if (streamReader.isNext([0x2c])) { 80 | streamReader.log("IMAGE DESCRIPTOR!"); 81 | if (!expectingImage) { 82 | // This is a bare image, not prefaced with a Graphics Control Extension 83 | // so we should treat it as a frame. 84 | frames.push({ index: streamReader.index, delay: 0 }); 85 | } 86 | expectingImage = false; 87 | 88 | streamReader.skipBytes(9); 89 | if (streamReader.peekBit(1)) { 90 | streamReader.log("LOCAL COLOR TABLE"); 91 | var colorTableSize = streamReader.readByte() & 0x07; 92 | streamReader.log("LOCAL COLOR TABLE IS " + 3 * Math.pow(2, colorTableSize + 1) + " BYTES") 93 | streamReader.skipBytes(3 * Math.pow(2, colorTableSize + 1)); 94 | } else { 95 | streamReader.log("NO LOCAL TABLE PHEW"); 96 | streamReader.skipBytes(1); 97 | } 98 | 99 | streamReader.log("MIN CODE SIZE " + streamReader.readByte()); 100 | streamReader.log("DATA START"); 101 | 102 | while (!streamReader.isNext([0x00])) { 103 | var blockSize = streamReader.readByte(); 104 | // streamReader.log("SKIPPING " + blockSize + " BYTES"); 105 | streamReader.skipBytes(blockSize); 106 | } 107 | streamReader.log("DATA END"); 108 | streamReader.skipBytes(1); //NULL terminator 109 | } else if (streamReader.isNext([0x21, 0xF9, 0x04])) { 110 | streamReader.log("GRAPHICS CONTROL EXTENSION!"); 111 | // We _definitely_ have a frame. Now we're expecting an image 112 | var index = streamReader.index; 113 | 114 | streamReader.skipBytes(3); 115 | var disposalMethod = streamReader.readByte() >> 2; 116 | streamReader.log("DISPOSAL " + disposalMethod); 117 | var delay = streamReader.readByte() + streamReader.readByte() * 256; 118 | frames.push({ index: index, delay: delay, disposal: disposalMethod }); 119 | streamReader.log("FRAME DELAY " + delay); 120 | streamReader.skipBytes(2); 121 | expectingImage = true; 122 | } else { 123 | var maybeTheEnd = streamReader.index; 124 | while (!streamReader.finished() && !streamReader.isNext([0x21, 0xF9, 0x04])) { 125 | streamReader.readByte(); 126 | } 127 | if (streamReader.finished()) { 128 | streamReader.index = maybeTheEnd; 129 | streamReader.log("WE END"); 130 | spinning = false; 131 | } else { 132 | streamReader.log("UNKNOWN DATA FROM " + maybeTheEnd); 133 | } 134 | } 135 | } 136 | var endOfFrames = streamReader.index; 137 | 138 | var gifFooter = buffer.slice(-1); //last bit is all we need 139 | for (var i = 0; i < frames.length; i++) { 140 | var frame = frames[i]; 141 | var nextIndex = (i < frames.length - 1) ? frames[i + 1].index : endOfFrames; 142 | frame.blob = new Blob([ gifHeader, buffer.slice(frame.index, nextIndex), gifFooter ], {type: 'image/gif'}); 143 | frame.url = url.createObjectURL(frame.blob); 144 | } 145 | 146 | resolve(new Gif(frames)); 147 | }) 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/gif.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var defaultFrameDelay = 10; 4 | 5 | export default class Gif { 6 | constructor(frames) { 7 | this.frames = frames; 8 | this.length = 0; 9 | this.offsets = [] 10 | 11 | frames.forEach((frame) => { 12 | this.offsets.push(this.length); 13 | this.length += (frame.delay || defaultFrameDelay); 14 | }); 15 | } 16 | 17 | frameAt(fraction) { 18 | var offset = fraction * this.length; 19 | for (var i = 1, l = this.offsets.length; i < l; i++) { 20 | if (this.offsets[i] > offset) break; 21 | } 22 | return i - 1; 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /src/playback.js: -------------------------------------------------------------------------------- 1 | import Exploder from './exploder.js'; 2 | 3 | // Private functions for setup 4 | function addClasses(element, frame) { 5 | element.classList.add('frame'); 6 | if (frame.disposal == 2) element.classList.add('disposal-restore'); 7 | } 8 | var createImage = function (frame) { 9 | var image = new Image(); 10 | image.src = frame.url; 11 | addClasses(image, frame); 12 | return image; 13 | }; 14 | 15 | export default class Playback { 16 | constructor(xgif, element, file, opts) { 17 | // Set up out instance variables 18 | this.xgif = xgif; 19 | this.element = element; 20 | this.onReady = opts.onReady; 21 | this.pingPong = opts.pingPong; 22 | this.fill = opts.fill; 23 | this.stopped = opts.stopped; 24 | this.snap = opts.snap; 25 | this.nTimes = opts.nTimes; 26 | 27 | this.ready = new Promise((resolve, reject) => { 28 | var exploder = new Exploder(file) 29 | exploder.load().then((gif) => { 30 | // Once we have the GIF data, add things to the DOM 31 | console.debug("Received " + gif.frames.length + " frames of gif " + file) 32 | this.gif = gif; 33 | 34 | this.element.innerHTML = ""; 35 | var createFrameElement = createImage;//(this.fill) ? createDiv : createImage; 36 | gif.frames.map(createFrameElement) 37 | .forEach(this.element.appendChild, this.element); 38 | 39 | if (this.fill) requestAnimationFrame(this.scaleToFill.bind(this)); 40 | 41 | resolve(); 42 | }); 43 | }) 44 | } 45 | 46 | scaleToFill() { 47 | if (!(this.element.offsetWidth && this.element.offsetHeight)) { 48 | requestAnimationFrame(this.scaleToFill.bind(this)); 49 | } else { 50 | var xScale = this.element.parentElement.offsetWidth / this.element.offsetWidth, 51 | yScale = this.element.parentElement.offsetHeight / this.element.offsetHeight; 52 | 53 | this.element.style.webkitTransform = "scale(" + 1.1 * Math.max(xScale, yScale) + ")"; 54 | } 55 | } 56 | 57 | setFrame(fraction, repeatCount) { 58 | var frameNr = (this.pingPong && repeatCount % 2 >= 1) ? this.gif.frameAt(1 - fraction) : this.gif.frameAt(fraction); 59 | this.element.dataset['frame'] = frameNr; 60 | } 61 | 62 | start() { 63 | this.stopped = false; 64 | this.startTime = performance.now(); 65 | if (this.animationLoop) this.animationLoop(); 66 | } 67 | 68 | stop() { 69 | this.stopped = true; 70 | } 71 | 72 | startSpeed(speed) { 73 | this.speed = speed; 74 | this.animationLoop = () => { 75 | // Calculate where we are in the GIF 76 | var gifLength = 10 * this.gif.length / this.speed, 77 | duration = performance.now() - this.startTime, 78 | repeatCount = duration / gifLength, 79 | fraction = repeatCount % 1; 80 | 81 | // If it's time to stop, set ourselves to the right frame (based on nTimes) 82 | // and fire an event (which adds the 'stopped' attribute) 83 | if (this.nTimes && repeatCount >= this.nTimes) { 84 | this.setFrame(this.nTimes % 1 || 1.0, repeatCount); 85 | this.element.dispatchEvent(new CustomEvent('x-gif-finished'), true); 86 | 87 | // Otherwise continue playing as normal, and request another animationFrame 88 | } else { 89 | this.setFrame(fraction, repeatCount); 90 | if (!this.stopped) requestAnimationFrame(this.animationLoop); 91 | } 92 | } 93 | 94 | if (!this.stopped) this.start(); 95 | } 96 | 97 | fromClock(beatNr, beatDuration, beatFraction) { 98 | // Always bias GIFs to speeding up rather than slowing down, it looks better. 99 | var speedup = 1.5, 100 | lengthInBeats = this.snap ? 1 : Math.max(1, Math.round((1 / speedup) * 10 * this.gif.length / beatDuration)), 101 | subBeat = beatNr % lengthInBeats, 102 | repeatCount = beatNr / lengthInBeats, 103 | subFraction = (beatFraction / lengthInBeats) + subBeat / lengthInBeats; 104 | this.setFrame(subFraction, repeatCount); 105 | } 106 | 107 | changeBpm(bpm) { 108 | this.beatLength = 60 * 1000 / bpm; 109 | } 110 | 111 | startBpm(bpm) { 112 | this.changeBpm(bpm); 113 | this.animationLoop = () => { 114 | var duration = performance.now() - this.startTime, 115 | beatNr = Math.floor(duration / this.beatLength), 116 | beatFraction = (duration % this.beatLength) / this.beatLength; 117 | 118 | this.fromClock(beatNr, this.beatLength, beatFraction); 119 | 120 | if (!this.stopped) requestAnimationFrame(this.animationLoop); 121 | } 122 | 123 | if (!this.stopped) this.start(); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/strategies.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var Strategies = { 4 | speed: function () { 5 | this.playback.startSpeed(this.speed, this.context.getAttribute('n-times')); 6 | }, 7 | hardBpm: function () { 8 | this.playback.startHardBpm(parseFloat(this.context.getAttribute('hard-bpm'))); 9 | }, 10 | bpm: function () { 11 | this.playback.startBpm(parseFloat(this.context.getAttribute('bpm'))); 12 | }, 13 | noop: function () { 14 | } 15 | }; 16 | 17 | export default Strategies; 18 | -------------------------------------------------------------------------------- /src/stream_reader.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | export default class StreamReader { 4 | constructor(arrayBuffer) { 5 | this.data = new Uint8Array(arrayBuffer); 6 | this.index = 0; 7 | this.log("TOTAL LENGTH: " + this.data.length); 8 | } 9 | 10 | finished() { 11 | return this.index >= this.data.length; 12 | } 13 | 14 | readByte() { 15 | return this.data[this.index++]; 16 | } 17 | 18 | peekByte() { 19 | return this.data[this.index]; 20 | } 21 | 22 | skipBytes(n) { 23 | this.index += n; 24 | } 25 | 26 | peekBit(i) { 27 | return !!(this.peekByte() & (1 << 8 - i)); 28 | } 29 | 30 | readAscii(n) { 31 | var s = ''; 32 | for (var i = 0; i < n; i++) { 33 | s += String.fromCharCode(this.readByte()); 34 | } 35 | return s; 36 | } 37 | 38 | isNext(array) { 39 | for (var i = 0; i < array.length; i++) { 40 | if (array[i] !== this.data[this.index + i]) return false; 41 | } 42 | return true; 43 | } 44 | 45 | log(str) { 46 | // console.log(this.index + ": " + str); 47 | } 48 | 49 | error(str) { 50 | console.error(this.index + ": " + str); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | export var Promises = { 4 | xhrGet: (url, accept, responseType) => { 5 | return new Promise((resolve, reject) => { 6 | var loader = new XMLHttpRequest(); 7 | loader.open('GET', url, true); 8 | loader.setRequestHeader("Accept", accept); 9 | loader.responseType = responseType; 10 | loader.onload = function() { 11 | // This is called even on 404 etc 12 | // so check the status 13 | if (this.status == 200) { 14 | // Resolve the promise with the response text 15 | resolve(this.response); 16 | } 17 | else { 18 | // Otherwise reject with the status text 19 | // which will hopefully be a meaningful error 20 | reject(Error(this.statusText)); 21 | } 22 | }; 23 | 24 | // Handle network errors 25 | loader.onerror = function() { 26 | reject(Error("Network Error")); 27 | }; 28 | loader.send(); 29 | }) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/x-gif.angular.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import Playback from './playback.js'; 4 | import Strategies from './strategies.js'; 5 | 6 | angular.module('x-gif', []) 7 | // Angular strips the 'x' off cause reasons 8 | .directive('gif', function () { 9 | return { 10 | restrict: 'E', 11 | template: '
', 12 | link: function (scope, element, attrs) { 13 | var xGif = Object.create(attrs, { 14 | fire: { 15 | value: function (event) { 16 | console.log(event); 17 | } 18 | } 19 | }); 20 | 21 | if (xGif.exploded != null) { 22 | xGif.playbackStrategy = 'noop' 23 | } else if (xGif.sync != null) { 24 | xGif.playbackStrategy = 'noop'; 25 | } else if (xGif.hardBpm) { 26 | xGif.playbackStrategy = 'hardBpm'; 27 | } else if (xGif.bpm) { 28 | xGif.playbackStrategy = 'bpm'; 29 | } else { 30 | xGif.speed = xGif.speed || 1.0; 31 | xGif.playbackStrategy = 'speed'; 32 | } 33 | 34 | attrs.$observe('src', function (src) { 35 | if (!src) return; 36 | var playbackStrategy = Strategies[xGif.playbackStrategy]; 37 | xGif.playback = new Playback(xGif, element[0].querySelector('.x-gif__frames'), xGif.src, { 38 | pingPong: xGif.pingPong != null, 39 | fill: xGif.fill != null, 40 | stopped: xGif.stopped != null 41 | }); 42 | xGif.playback.ready.then(playbackStrategy.bind(xGif)); 43 | }) 44 | 45 | attrs.$observe('speed', function (speed) { 46 | if (!speed) return; 47 | if (xGif.playback) xGif.playback.speed = speed; 48 | }); 49 | 50 | element[0].clock = function (beatNr, beatDuration, beatFraction) { 51 | if (xGif.playback && xGif.playback.gif) xGif.playback.fromClock(beatNr, beatDuration, beatFraction); 52 | } 53 | 54 | element[0].relayout = function () { 55 | if (xGif.playback && xGif.fill != null) xGif.playback.scaleToFill(); 56 | } 57 | } 58 | } 59 | }); 60 | -------------------------------------------------------------------------------- /src/x-gif.html: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/x-gif.js: -------------------------------------------------------------------------------- 1 | import Playback from './playback.js'; 2 | import Strategies from './strategies.js'; 3 | 4 | // Shim & native-safe ownerDocument lookup 5 | var owner = (document._currentScript || document.currentScript).ownerDocument; 6 | 7 | class XGifController { 8 | constructor(xgif) { 9 | this.xgif = xgif; 10 | this.setupComponent(); 11 | this.srcChanged(this.xgif.getAttribute('src')) 12 | } 13 | 14 | setupComponent() { 15 | // Create a shadow root 16 | this.shadow = this.xgif.createShadowRoot(); 17 | 18 | // stamp out our template in the shadow dom 19 | var template = owner.querySelector("#template").content.cloneNode(true); 20 | this.shadow.appendChild(template); 21 | } 22 | 23 | srcChanged(src) { 24 | if (!src) return; 25 | console.log("Loading " + src); 26 | this.playback = new Playback(this, this.shadow.querySelector('#frames'), src, this.xgif.options); 27 | this.playback.ready.then(() => { 28 | if (this.xgif.playbackMode == 'speed') { 29 | this.playback.startSpeed(this.xgif.speed); 30 | } else if (this.xgif.playbackMode == 'bpm') { 31 | this.playback.startBpm(this.xgif.bpm); 32 | } 33 | }); 34 | } 35 | 36 | speedChanged(speed) { 37 | if (this.playback) this.playback.speed = speed; 38 | } 39 | 40 | bpmChanged(bpm) { 41 | if (this.playback) this.playback.changeBpm(bpm); 42 | } 43 | 44 | snapChanged(snap) { 45 | if (this.playback) this.playback.snap = snap; 46 | } 47 | 48 | nTimesChanged(nTimes) { 49 | if (this.playback) this.playback.nTimes = nTimes; 50 | } 51 | 52 | stoppedChanged(nowStop) { 53 | if (this.playback) { 54 | if (nowStop && !this.playback.stopped) { 55 | this.playback.stop(); 56 | } else if (!nowStop && this.playback.stopped) { 57 | this.playback.start(); 58 | } 59 | } 60 | } 61 | 62 | pingPongChanged(nowPingPong) { 63 | if (this.playback) this.playback.pingPong = nowPingPong; 64 | } 65 | 66 | clock(beatNr, beatDuration, beatFraction) { 67 | if (this.playback && this.playback.gif) { 68 | this.playback.fromClock(beatNr, beatDuration, beatFraction); 69 | } 70 | } 71 | 72 | relayout() { 73 | if (this.playback && this.xgif.options.fill) { 74 | this.playback.scaleToFill(); 75 | } 76 | } 77 | } 78 | 79 | // Register the element in the document 80 | class XGif extends HTMLElement { 81 | createdCallback() { 82 | this.determinePlaybackMode() 83 | this.determinePlaybackOptions() 84 | this.addStoppedOnNTimesFinishing() 85 | this.controller = new XGifController(this); 86 | } 87 | 88 | determinePlaybackMode() { 89 | // We might not want x-gif to animate itself at all 90 | if (this.hasAttribute('exploded') || this.hasAttribute('sync')) { 91 | this.playbackMode = undefined; 92 | return; 93 | } 94 | 95 | // BPM Mode 96 | var maybeBPM = parseFloat(this.getAttribute('bpm')) 97 | if (!isNaN(maybeBPM)) { 98 | this.playbackMode = 'bpm'; 99 | this.bpm = maybeBPM; 100 | return; 101 | } 102 | 103 | // Default to THE BUS THAT COULDNT SLOW DOWN mode 104 | var maybeSpeed = parseFloat(this.getAttribute('speed')) 105 | this.speed = isNaN(maybeSpeed) ? 1.0 : maybeSpeed; 106 | this.playbackMode = 'speed'; 107 | } 108 | 109 | determinePlaybackOptions() { 110 | var maybeNtimes = parseFloat(this.getAttribute('n-times')) 111 | this.options = { 112 | stopped: this.hasAttribute('stopped'), 113 | fill: this.hasAttribute('fill'), 114 | nTimes: isNaN(maybeNtimes) ? null : maybeNtimes, 115 | snap: this.hasAttribute('snap'), 116 | pingPong: this.hasAttribute('ping-pong') 117 | } 118 | } 119 | 120 | attributeChangedCallback(attribute, oldVal, newVal) { 121 | if (attribute == "src") { 122 | this.controller.srcChanged(newVal) 123 | } else if (attribute == "speed") { 124 | this.determinePlaybackMode(); 125 | this.controller.speedChanged(this.speed); 126 | } else if (attribute == "bpm") { 127 | this.determinePlaybackMode(); 128 | this.controller.bpmChanged(this.bpm); 129 | } else if (attribute == "stopped") { 130 | this.determinePlaybackOptions(); 131 | this.controller.stoppedChanged(this.options.stopped); 132 | } else if (attribute == "ping-pong") { 133 | this.determinePlaybackOptions(); 134 | this.controller.pingPongChanged(this.options.pingPong); 135 | } else if (attribute == "snap") { 136 | this.determinePlaybackOptions(); 137 | this.controller.snapChanged(this.options.snap); 138 | } else if (attribute == "n-times") { 139 | this.determinePlaybackOptions(); 140 | this.controller.nTimesChanged(this.options.nTimes); 141 | } 142 | } 143 | 144 | clock(beatNr, beatDuration, beatFraction) { 145 | this.controller.clock(beatNr, beatDuration, beatFraction) 146 | } 147 | 148 | relayout() { 149 | this.controller.relayout(); 150 | } 151 | 152 | addStoppedOnNTimesFinishing() { 153 | this.addEventListener('x-gif-finished', () => { 154 | this.setAttribute('stopped', '') 155 | }) 156 | } 157 | } 158 | 159 | // Register our todo-item tag with the document 160 | document.registerElement('x-gif', XGif); 161 | -------------------------------------------------------------------------------- /src/x-gif.polymer.js: -------------------------------------------------------------------------------- 1 | import Playback from './playback.js'; 2 | import Strategies from './strategies.js'; 3 | 4 | class XGif { 5 | ready() { 6 | if (this.exploded != null) { 7 | this.playbackStrategy = 'noop' 8 | } else if (this.sync != null) { 9 | this.playbackStrategy = 'noop'; 10 | } else if (this['hard-bpm']) { 11 | this.playbackStrategy = 'hardBpm'; 12 | } else if (this.bpm) { 13 | this.playbackStrategy = 'bpm'; 14 | } else { 15 | this.speed = this.speed || 1.0; 16 | this.playbackStrategy = 'speed'; 17 | } 18 | }; 19 | 20 | srcChanged() { 21 | var playbackStrategy = Strategies[this.playbackStrategy]; 22 | this.playback = new Playback(this, this.$.frames, this.src, { 23 | pingPong: this['ping-pong'] != null, 24 | fill: this.fill != null, 25 | stopped: this.stopped != null 26 | }); 27 | this.playback.ready.then(playbackStrategy.bind(this)); 28 | }; 29 | 30 | speedChanged(oldVal, newVal) { 31 | if (this.playback) this.playback.speed = newVal; 32 | } 33 | 34 | stoppedChanged(oldVal, newVal) { 35 | var nowStop = newVal != null; 36 | if (this.playback && nowStop && !this.playback.stopped) { 37 | this.playback.stop(); 38 | } else if (this.playback && !nowStop && this.playback.stopped) { 39 | this.playback.start(); 40 | } 41 | } 42 | 43 | togglePingPong() { 44 | this['ping-pong'] = (this['ping-pong'] != null) ? null : true; 45 | if (this.playback) this.playback.pingPong = this['ping-pong'] != null; 46 | } 47 | 48 | clock(beatNr, beatDuration, beatFraction) { 49 | if (this.playback && this.playback.gif) this.playback.fromClock(beatNr, beatDuration, beatFraction); 50 | }; 51 | 52 | relayout() { 53 | if (this.fill != null) this.playback.scaleToFill(); 54 | } 55 | } 56 | 57 | Polymer('x-gif', new XGif()); 58 | -------------------------------------------------------------------------------- /src/x-gif.scss: -------------------------------------------------------------------------------- 1 | :host { 2 | display: inline-block; 3 | } 4 | 5 | #frames, 6 | .x-gif__frames { 7 | display: inline-block; 8 | position: relative; 9 | 10 | @for $i from 0 to 100 { 11 | &[data-frame="#{$i}"] { 12 | .frame:nth-child(n + #{2 + $i}) { 13 | opacity: 0; 14 | } 15 | .frame.disposal-restore:nth-child(#{1 + $i}) { 16 | opacity: 1; 17 | } 18 | } 19 | } 20 | } 21 | 22 | .frame { 23 | position: absolute; 24 | top: 0; 25 | left: 0; 26 | transform: translateZ(0); 27 | 28 | &:first-child { 29 | position: static; 30 | } 31 | &.disposal-restore { 32 | opacity: 0; 33 | } 34 | 35 | :host([exploded]) & { 36 | position: static; 37 | opacity: 1; 38 | } 39 | } 40 | 41 | x-gif { 42 | // display: none; 43 | } 44 | 45 | // Full Screen overrides 46 | @mixin fill-overrides { 47 | width: 100%; 48 | height: 100%; 49 | 50 | .frames-wrapper { 51 | width: 100%; 52 | height: 100%; 53 | display: flex; 54 | justify-content: center; 55 | align-items: center; 56 | overflow: hidden; 57 | } 58 | 59 | #frames, 60 | .x-gif__frames { 61 | transform-origin: 50% 50%; 62 | } 63 | } 64 | 65 | :host([fill]) { 66 | @include fill-overrides; 67 | } 68 | 69 | x-gif[fill] { 70 | @include fill-overrides; 71 | } 72 | --------------------------------------------------------------------------------