├── README ├── audio_scripts ├── anim.pde ├── beatdetektor.js ├── dsp.js ├── main.js └── processing.js ├── demo.html ├── demos.css ├── fonts ├── BebasNeue.otf ├── MEgalopolisExtra.woff ├── MarketingScript.ttf └── ubuntu.ttf ├── imgs ├── bgr.jpg ├── close.png ├── controls.jpg ├── dashed.png ├── link.png ├── lion.svg ├── overlay.png ├── phial.svg ├── progress.png ├── texture.png ├── throbber.gif ├── tiger.svg ├── tv-border.jpg └── whiteButton.png ├── logos ├── android.png ├── apple.png ├── linux.png ├── maemo.png ├── meego.png ├── w3c.png ├── webm.png ├── windows.png └── xiph.png ├── media ├── enfants.webm └── track.ogg ├── screenshot.jpg ├── scripts ├── audioplayer.js ├── canvas2d.js ├── classList.js ├── dnd.js ├── geolocation.js ├── jquery.jplayer.min.js ├── jquery.rotate.js ├── main.js └── orientation.js ├── style.css └── webgl_assets ├── Teapot.json ├── glUtils.js ├── gldemo.js ├── sylvester.js └── texture.jpg /README: -------------------------------------------------------------------------------- 1 | HTML5 Dashboard! 2 | -------------------------------------------------------------------------------- /audio_scripts/anim.pde: -------------------------------------------------------------------------------- 1 | int x_step; 2 | int x_width; 3 | int bar_height; 4 | int width = 128; 5 | int height = 128; 6 | 7 | void setup() { 8 | size(128, 128); 9 | noLoop(); 10 | } 11 | 12 | void draw() { 13 | background(0, 0, 0, 0); 14 | noStroke(); 15 | fill(225, 230, 233, 255); 16 | 17 | if ( bd.beat_counter % 4 == 0 ) { 18 | rect(2, 2, 60, 60); 19 | } else if ( bd.beat_counter % 4 == 1 ) { 20 | rect(2, 66, 60, 60); 21 | } else if ( bd.beat_counter % 4 == 2 ) { 22 | rect(66, 66, 60, 60); 23 | } else if ( bd.beat_counter % 4 == 3 ) { 24 | rect(66, 2, 60, 60); 25 | } 26 | 27 | strokeWeight(0.0); 28 | 29 | if (vu.vu_levels.length) { 30 | int x_step = (width/(bd.config.BD_DETECTION_RANGES/4)); 31 | int x_width = (width/(bd.config.BD_DETECTION_RANGES/4))-2; 32 | 33 | fill(108, 207, 228, 255); 34 | for (int i = 0; i < bd.config.BD_DETECTION_RANGES/4; i++) { 35 | int v_i = i; 36 | 37 | int sz; 38 | sz = 64*vu.vu_levels[v_i]; 39 | if (sz>64) sz = 64; 40 | 41 | rect(i*x_step,128-sz,2,sz); 42 | } 43 | } 44 | 45 | strokeWeight(3); 46 | stroke(108, 207, 228, 255); 47 | 48 | for ( int i = 0; i < 128; i++) { 49 | float mi = Math.abs(signal[4*i]); 50 | line(i, 32 - signal[4*i] * 32, i + 1, 32 - signal[4*(i+1)] * 32) 51 | } 52 | } 53 | 54 | -------------------------------------------------------------------------------- /audio_scripts/beatdetektor.js: -------------------------------------------------------------------------------- 1 | /* 2 | * BeatDetektor.js 3 | * 4 | * BeatDetektor - CubicFX Visualizer Beat Detection & Analysis Algorithm 5 | * Javascript port by Charles J. Cliffe and Corban Brook 6 | * 7 | * Created by Charles J. Cliffe on 09-11-30. 8 | * Copyright 2009 Charles J. Cliffe. All rights reserved. 9 | * 10 | * BeatDetektor is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU Lesser General Public License as published by 12 | * the Free Software Foundation, either version 3 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * Please note that only the Javascript version of BeatDetektor is licensed 16 | * under the terms of LGPL version 3; ports of BeatDetektor or derivatives 17 | * in other languages are licensed under the terms of GPL version 3. 18 | * 19 | * BeatDetektor is distributed in the hope that it will be useful, 20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | * GNU Lesser General Public License for more details. 23 | * 24 | * You should have received a copy of the GNU Lesser General Public License 25 | * along with this program. If not, see . 26 | * 27 | * Please contact cj@cubicproductions.com if you seek alternate 28 | * licensing terms for your project. 29 | * 30 | */ 31 | 32 | 33 | /* 34 | BeatDetektor class 35 | 36 | 37 | Theory: 38 | 39 | Trigger detection is performed using a trail of moving averages, 40 | 41 | The FFT input is broken up into 128 ranges and averaged, each range has two moving 42 | averages that tail each other at a rate of (1.0 / BD_DETECTION_RATE) seconds. 43 | 44 | Each time the moving average for a range exceeds it's own tailing average by: 45 | 46 | (moving_average[range] * BD_DETECTION_FACTOR >= moving_average[range]) 47 | 48 | if this is true there's a rising edge and a detection is flagged for that range. 49 | Next a trigger gap test is performed between rising edges and timestamp recorded. 50 | 51 | If the gap is larger than our BPM window (in seconds) then we can discard it and 52 | reset the timestamp for a new detection -- but only after checking to see if it's a 53 | reasonable match for 2* the current detection in case it's only triggered every 54 | other beat. Gaps that are lower than the BPM window are ignored and the last 55 | timestamp will not be reset. 56 | 57 | Gaps that are within a reasonable window are run through a quality stage to determine 58 | how 'close' they are to that channel's current prediction and are incremented or 59 | decremented by a weighted value depending on accuracy. Repeated hits of low accuracy 60 | will still move a value towards erroneous detection but it's quality will be lowered 61 | and will not be eligible for the gap time quality draft. 62 | 63 | Once quality has been assigned ranges are reviewed for good match candidates and if 64 | BD_MINIMUM_CONTRIBUTIONS or more ranges achieve a decent ratio (with a factor of 65 | BD_QUALITY_TOLERANCE) of contribution to the overall quality we take them into the 66 | contest round. Note that the contest round won't run on a given process() call if 67 | the total quality achieved does not meet or exceed BD_QUALITY_TOLERANCE. 68 | 69 | Each time through if a select draft of BPM ranges has achieved a reasonable quality 70 | above others it's awarded a value in the BPM contest. The BPM contest is a hash 71 | array indexed by an integer BPM value, each draft winner is awarded BD_QUALITY_REWARD. 72 | 73 | Finally the BPM contest is examined to determine a leader and all contest entries 74 | are normalized to a total value of BD_FINISH_LINE, whichever range is closest to 75 | BD_FINISH_LINE at any given point is considered to be the best guess however waiting 76 | until a minimum contest winning value of about 20.0-25.0 will provide more accurate 77 | results. Note that the 20-25 rule may vary with lower and higher input ranges. 78 | A winning value that exceeds 40 or hovers around 60 (the finish line) is pretty much 79 | a guaranteed match. 80 | 81 | 82 | Configuration Kernel Notes: 83 | 84 | The majority of the ratios and values have been reverse-engineered from my own 85 | observation and visualization of information from various aspects of the detection 86 | triggers; so not all parameters have a perfect definition nor perhaps the best value yet. 87 | However despite this it performs very well; I had expected several more layers 88 | before a reasonable detection would be achieved. Comments for these parameters will be 89 | updated as analysis of their direct effect is explored. 90 | 91 | 92 | Input Restrictions: 93 | 94 | bpm_maximum must be within the range of (bpm_minimum*2)-1 95 | i.e. minimum of 50 must have a maximum of 99 because 50*2 = 100 96 | 97 | 98 | Changelog: 99 | 100 | 01/17/2010 - Charles J. Cliffe 101 | - Tested and tweaked default kernel values for tighter detection 102 | - Added BeatDetektor.config_48_95, BeatDetektor.config_90_179 and BeatDetektor.config_150_280 for more refined detection ranges 103 | - Updated unit test to include new range config example 104 | 105 | 02/21/2010 - Charles J. Cliffe 106 | - Fixed numerous bugs and divide by 0 on 1% match causing poor accuracy 107 | - Re-worked the quality calulations, accuracy improved 8-10x 108 | - Primary value is now a fractional reading (*10, just divide by 10), added win_bpm_int_lo for integral readings 109 | - Added feedback loop for current_bpm to help back-up low quality channels 110 | - Unified range configs, now single default should be fine 111 | - Extended quality reward 'funnel' 112 | 113 | */ 114 | BeatDetektor = function(bpm_minimum, bpm_maximum, alt_config) 115 | { 116 | if (typeof(bpm_minimum)=='undefined') bpm_minimum = 85.0; 117 | if (typeof(bpm_maximum)=='undefined') bpm_maximum = 169.0 118 | 119 | this.config = (typeof(alt_config)!='undefined')?alt_config:BeatDetektor.config; 120 | 121 | this.BPM_MIN = bpm_minimum; 122 | this.BPM_MAX = bpm_maximum; 123 | 124 | this.beat_counter = 0; 125 | this.half_counter = 0; 126 | this.quarter_counter = 0; 127 | 128 | // current average (this sample) for range n 129 | this.a_freq_range = new Array(this.config.BD_DETECTION_RANGES); 130 | // moving average of frequency range n 131 | this.ma_freq_range = new Array(this.config.BD_DETECTION_RANGES); 132 | // moving average of moving average of frequency range n 133 | this.maa_freq_range = new Array(this.config.BD_DETECTION_RANGES); 134 | // timestamp of last detection for frequecy range n 135 | this.last_detection = new Array(this.config.BD_DETECTION_RANGES); 136 | 137 | // moving average of gap lengths 138 | this.ma_bpm_range = new Array(this.config.BD_DETECTION_RANGES); 139 | // moving average of moving average of gap lengths 140 | this.maa_bpm_range = new Array(this.config.BD_DETECTION_RANGES); 141 | 142 | // range n quality attribute, good match = quality+, bad match = quality-, min = 0 143 | this.detection_quality = new Array(this.config.BD_DETECTION_RANGES); 144 | 145 | // current trigger state for range n 146 | this.detection = new Array(this.config.BD_DETECTION_RANGES); 147 | 148 | this.reset(); 149 | 150 | if (typeof(console)!='undefined') 151 | { 152 | console.log("BeatDetektor("+this.BPM_MIN+","+this.BPM_MAX+") created.") 153 | } 154 | } 155 | 156 | BeatDetektor.prototype.reset = function() 157 | { 158 | // var bpm_avg = 60.0/((this.BPM_MIN+this.BPM_MAX)/2.0); 159 | 160 | for (var i = 0; i < this.config.BD_DETECTION_RANGES; i++) 161 | { 162 | this.a_freq_range[i] = 0.0; 163 | this.ma_freq_range[i] = 0.0; 164 | this.maa_freq_range[i] = 0.0; 165 | this.last_detection[i] = 0.0; 166 | 167 | this.ma_bpm_range[i] = 168 | this.maa_bpm_range[i] = 60.0/this.BPM_MIN + ((60.0/this.BPM_MAX-60.0/this.BPM_MIN) * (i/this.config.BD_DETECTION_RANGES)); 169 | 170 | this.detection_quality[i] = 0.0; 171 | this.detection[i] = false; 172 | } 173 | 174 | this.ma_quality_avg = 0; 175 | this.ma_quality_total = 0; 176 | 177 | this.bpm_contest = new Array(); 178 | this.bpm_contest_lo = new Array(); 179 | 180 | this.quality_total = 0.0; 181 | this.quality_avg = 0.0; 182 | 183 | this.current_bpm = 0.0; 184 | this.current_bpm_lo = 0.0; 185 | 186 | this.winning_bpm = 0.0; 187 | this.win_val = 0.0; 188 | this.winning_bpm_lo = 0.0; 189 | this.win_val_lo = 0.0; 190 | 191 | this.win_bpm_int = 0; 192 | this.win_bpm_int_lo = 0; 193 | 194 | this.bpm_predict = 0; 195 | 196 | this.is_erratic = false; 197 | this.bpm_offset = 0.0; 198 | this.last_timer = 0.0; 199 | this.last_update = 0.0; 200 | 201 | this.bpm_timer = 0.0; 202 | this.beat_counter = 0; 203 | this.half_counter = 0; 204 | this.quarter_counter = 0; 205 | } 206 | 207 | 208 | BeatDetektor.config_default = { 209 | BD_DETECTION_RANGES : 128, // How many ranges to quantize the FFT into 210 | BD_DETECTION_RATE : 12.0, // Rate in 1.0 / BD_DETECTION_RATE seconds 211 | BD_DETECTION_FACTOR : 0.915, // Trigger ratio 212 | BD_QUALITY_DECAY : 0.6, // range and contest decay 213 | BD_QUALITY_TOLERANCE : 0.96,// Use the top x % of contest results 214 | BD_QUALITY_REWARD : 10.0, // Award weight 215 | BD_QUALITY_STEP : 0.1, // Award step (roaming speed) 216 | BD_MINIMUM_CONTRIBUTIONS : 6, // At least x ranges must agree to process a result 217 | BD_FINISH_LINE : 60.0, // Contest values wil be normalized to this finish line 218 | // this is the 'funnel' that pulls ranges in / out of alignment based on trigger detection 219 | BD_REWARD_TOLERANCES : [ 0.001, 0.005, 0.01, 0.02, 0.04, 0.08, 0.10, 0.15, 0.30 ], // .1%, .5%, 1%, 2%, 4%, 8%, 10%, 15% 220 | BD_REWARD_MULTIPLIERS : [ 20.0, 10.0, 8.0, 1.0, 1.0/2.0, 1.0/4.0, 1.0/8.0, 1/16.0, 1/32.0 ] 221 | }; 222 | 223 | 224 | // Default configuration kernel 225 | BeatDetektor.config = BeatDetektor.config_default; 226 | 227 | 228 | BeatDetektor.prototype.process = function(timer_seconds, fft_data) 229 | { 230 | if (!this.last_timer) { this.last_timer = timer_seconds; return; } // ignore 0 start time 231 | if (this.last_timer > timer_seconds) { this.reset(); return; } 232 | 233 | var timestamp = timer_seconds; 234 | 235 | this.last_update = timer_seconds - this.last_timer; 236 | this.last_timer = timer_seconds; 237 | 238 | if (this.last_update > 1.0) { this.reset(); return; } 239 | 240 | var i,x; 241 | var v; 242 | 243 | var bpm_floor = 60.0/this.BPM_MAX; 244 | var bpm_ceil = 60.0/this.BPM_MIN; 245 | 246 | var range_step = (fft_data.length / this.config.BD_DETECTION_RANGES); 247 | var range = 0; 248 | 249 | 250 | for (x=0; x= this.maa_freq_range[range]); 273 | 274 | // compute bpm clamps for comparison to gap lengths 275 | 276 | // clamp detection averages to input ranges 277 | if (this.ma_bpm_range[range] > bpm_ceil) this.ma_bpm_range[range] = bpm_ceil; 278 | if (this.ma_bpm_range[range] < bpm_floor) this.ma_bpm_range[range] = bpm_floor; 279 | if (this.maa_bpm_range[range] > bpm_ceil) this.maa_bpm_range[range] = bpm_ceil; 280 | if (this.maa_bpm_range[range] < bpm_floor) this.maa_bpm_range[range] = bpm_floor; 281 | 282 | var rewarded = false; 283 | 284 | // new detection since last, test it's quality 285 | if (!this.detection[range] && det) 286 | { 287 | // calculate length of gap (since start of last trigger) 288 | var trigger_gap = timestamp-this.last_detection[range]; 289 | 290 | // trigger falls within acceptable range, 291 | if (trigger_gap < bpm_ceil && trigger_gap > (bpm_floor)) 292 | { 293 | // compute gap and award quality 294 | 295 | // use our tolerances as a funnel to edge detection towards the most likely value 296 | for (i = 0; i < this.config.BD_REWARD_TOLERANCES.length; i++) 297 | { 298 | if (Math.abs(this.ma_bpm_range[range]-trigger_gap) < this.ma_bpm_range[range]*this.config.BD_REWARD_TOLERANCES[i]) 299 | { 300 | this.detection_quality[range] += this.config.BD_QUALITY_REWARD * this.config.BD_REWARD_MULTIPLIERS[i]; 301 | rewarded = true; 302 | } 303 | } 304 | 305 | if (rewarded) 306 | { 307 | this.last_detection[range] = timestamp; 308 | } 309 | } 310 | else if (trigger_gap >= bpm_ceil) // low quality, gap exceeds maximum time 311 | { 312 | // start a new gap test, next gap is guaranteed to be longer 313 | 314 | // test for 1/2 beat 315 | trigger_gap /= 2.0; 316 | 317 | if (trigger_gap < bpm_ceil && trigger_gap > (bpm_floor)) for (i = 0; i < this.config.BD_REWARD_TOLERANCES.length; i++) 318 | { 319 | if (Math.abs(this.ma_bpm_range[range]-trigger_gap) < this.ma_bpm_range[range]*this.config.BD_REWARD_TOLERANCES[i]) 320 | { 321 | this.detection_quality[range] += this.config.BD_QUALITY_REWARD * this.config.BD_REWARD_MULTIPLIERS[i]; 322 | rewarded = true; 323 | } 324 | } 325 | 326 | 327 | // decrement quality if no 1/2 beat reward 328 | if (!rewarded) 329 | { 330 | trigger_gap *= 2.0; 331 | } 332 | this.last_detection[range] = timestamp; 333 | } 334 | 335 | if (rewarded) 336 | { 337 | var qmp = (this.detection_quality[range]/this.quality_avg)*this.config.BD_QUALITY_STEP; 338 | if (qmp > 1.0) 339 | { 340 | qmp = 1.0; 341 | } 342 | 343 | this.ma_bpm_range[range] -= (this.ma_bpm_range[range]-trigger_gap) * qmp; 344 | this.maa_bpm_range[range] -= (this.maa_bpm_range[range]-this.ma_bpm_range[range]) * qmp; 345 | } 346 | else if (trigger_gap >= bpm_floor && trigger_gap <= bpm_ceil) 347 | { 348 | if (this.detection_quality[range] < this.quality_avg*this.config.BD_QUALITY_TOLERANCE && this.current_bpm) 349 | { 350 | this.ma_bpm_range[range] -= (this.ma_bpm_range[range]-trigger_gap) * this.config.BD_QUALITY_STEP; 351 | this.maa_bpm_range[range] -= (this.maa_bpm_range[range]-this.ma_bpm_range[range]) * this.config.BD_QUALITY_STEP; 352 | } 353 | this.detection_quality[range] -= this.config.BD_QUALITY_STEP; 354 | } 355 | else if (trigger_gap >= bpm_ceil) 356 | { 357 | if ((this.detection_quality[range] < this.quality_avg*this.config.BD_QUALITY_TOLERANCE) && this.current_bpm) 358 | { 359 | this.ma_bpm_range[range] -= (this.ma_bpm_range[range]-this.current_bpm) * 0.5; 360 | this.maa_bpm_range[range] -= (this.maa_bpm_range[range]-this.ma_bpm_range[range]) * 0.5 ; 361 | } 362 | this.detection_quality[range]-= this.config.BD_QUALITY_STEP; 363 | } 364 | 365 | } 366 | 367 | if ((!rewarded && timestamp-this.last_detection[range] > bpm_ceil) || (det && Math.abs(this.ma_bpm_range[range]-this.current_bpm) > this.bpm_offset)) 368 | this.detection_quality[range] -= this.detection_quality[range]*this.config.BD_QUALITY_STEP*this.config.BD_QUALITY_DECAY*this.last_update; 369 | 370 | // quality bottomed out, set to 0 371 | if (this.detection_quality[range] < 0.001) this.detection_quality[range]=0.001; 372 | 373 | this.detection[range] = det; 374 | 375 | range++; 376 | } 377 | 378 | // total contribution weight 379 | this.quality_total = 0; 380 | 381 | // total of bpm values 382 | var bpm_total = 0; 383 | // number of bpm ranges that contributed to this test 384 | var bpm_contributions = 0; 385 | 386 | 387 | // accumulate quality weight total 388 | for (var x=0; x= this.ma_quality_avg) 423 | { 424 | if (this.ma_bpm_range[x] < bpm_ceil && this.ma_bpm_range[x] > bpm_floor) 425 | { 426 | bpm_total += this.maa_bpm_range[x]; 427 | 428 | var draft_float = Math.round((60.0/this.maa_bpm_range[x])*1000.0); 429 | 430 | draft_float = (Math.abs(Math.ceil(draft_float)-(60.0/this.current_bpm)*1000.0)<(Math.abs(Math.floor(draft_float)-(60.0/this.current_bpm)*1000.0)))?Math.ceil(draft_float/10.0):Math.floor(draft_float/10.0); 431 | var draft_int = parseInt(draft_float/10.0); 432 | // if (draft_int) console.log(draft_int); 433 | if (typeof(draft[draft_int]=='undefined')) draft[draft_int] = 0; 434 | 435 | draft[draft_int]+=this.detection_quality[x]/this.quality_avg; 436 | bpm_contributions++; 437 | if (offset_test_bpm == 0.0) offset_test_bpm = this.maa_bpm_range[x]; 438 | else 439 | { 440 | avg_bpm_offset += Math.abs(offset_test_bpm-this.maa_bpm_range[x]); 441 | } 442 | 443 | 444 | } 445 | } 446 | } 447 | 448 | // if we have one or more contributions that pass criteria then attempt to display a guess 449 | var has_prediction = (bpm_contributions>=this.config.BD_MINIMUM_CONTRIBUTIONS)?true:false; 450 | 451 | var draft_winner=0; 452 | var win_val = 0; 453 | 454 | if (has_prediction) 455 | { 456 | for (var draft_i in draft) 457 | { 458 | if (draft[draft_i] > win_val) 459 | { 460 | win_val = draft[draft_i]; 461 | draft_winner = draft_i; 462 | } 463 | } 464 | 465 | this.bpm_predict = 60.0/(draft_winner/10.0); 466 | 467 | avg_bpm_offset /= bpm_contributions; 468 | this.bpm_offset = avg_bpm_offset; 469 | 470 | if (!this.current_bpm) 471 | { 472 | this.current_bpm = this.bpm_predict; 473 | } 474 | } 475 | 476 | if (this.current_bpm && this.bpm_predict) this.current_bpm -= (this.current_bpm-this.bpm_predict)*this.last_update; 477 | 478 | // hold a contest for bpm to find the current mode 479 | var contest_max=0; 480 | 481 | for (var contest_i in this.bpm_contest) 482 | { 483 | if (contest_max < this.bpm_contest[contest_i]) contest_max = this.bpm_contest[contest_i]; 484 | if (this.bpm_contest[contest_i] > this.config.BD_FINISH_LINE/2.0) 485 | { 486 | var draft_int_lo = parseInt(Math.round((contest_i)/10.0)); 487 | if (this.bpm_contest_lo[draft_int_lo] != this.bpm_contest_lo[draft_int_lo]) this.bpm_contest_lo[draft_int_lo] = 0; 488 | this.bpm_contest_lo[draft_int_lo]+= (this.bpm_contest[contest_i]/6.0)*this.last_update; 489 | } 490 | } 491 | 492 | // normalize to a finish line 493 | if (contest_max > this.config.BD_FINISH_LINE) 494 | { 495 | for (var contest_i in this.bpm_contest) 496 | { 497 | this.bpm_contest[contest_i]=(this.bpm_contest[contest_i]/contest_max)*this.config.BD_FINISH_LINE; 498 | } 499 | } 500 | 501 | contest_max = 0; 502 | for (var contest_i in this.bpm_contest_lo) 503 | { 504 | if (contest_max < this.bpm_contest_lo[contest_i]) contest_max = this.bpm_contest_lo[contest_i]; 505 | } 506 | 507 | // normalize to a finish line 508 | if (contest_max > this.config.BD_FINISH_LINE) 509 | { 510 | for (var contest_i in this.bpm_contest_lo) 511 | { 512 | this.bpm_contest_lo[contest_i]=(this.bpm_contest_lo[contest_i]/contest_max)*this.config.BD_FINISH_LINE; 513 | } 514 | } 515 | 516 | 517 | // decay contest values from last loop 518 | for (contest_i in this.bpm_contest) 519 | { 520 | this.bpm_contest[contest_i]-=this.bpm_contest[contest_i]*(this.last_update/this.config.BD_DETECTION_RATE); 521 | } 522 | 523 | // decay contest values from last loop 524 | for (contest_i in this.bpm_contest_lo) 525 | { 526 | this.bpm_contest_lo[contest_i]-=this.bpm_contest_lo[contest_i]*(this.last_update/this.config.BD_DETECTION_RATE); 527 | } 528 | 529 | this.bpm_timer+=this.last_update; 530 | 531 | var winner = 0; 532 | var winner_lo = 0; 533 | 534 | // attempt to display the beat at the beat interval ;) 535 | if (this.bpm_timer > this.winning_bpm/4.0 && this.current_bpm) 536 | { 537 | this.win_val = 0; 538 | this.win_val_lo = 0; 539 | 540 | if (this.winning_bpm) while (this.bpm_timer > this.winning_bpm/4.0) this.bpm_timer -= this.winning_bpm/4.0; 541 | 542 | // increment beat counter 543 | 544 | this.quarter_counter++; 545 | this.half_counter= parseInt(this.quarter_counter/2); 546 | this.beat_counter = parseInt(this.quarter_counter/4); 547 | 548 | // award the winner of this iteration 549 | var idx = parseInt(Math.round((60.0/this.current_bpm)*10.0)); 550 | if (typeof(this.bpm_contest[idx])=='undefined') this.bpm_contest[idx] = 0; 551 | this.bpm_contest[idx]+=this.config.BD_QUALITY_REWARD; 552 | 553 | 554 | // find the overall winner so far 555 | for (var contest_i in this.bpm_contest) 556 | { 557 | if (this.win_val < this.bpm_contest[contest_i]) 558 | { 559 | winner = contest_i; 560 | this.win_val = this.bpm_contest[contest_i]; 561 | } 562 | } 563 | 564 | if (winner) 565 | { 566 | this.win_bpm_int = parseInt(winner); 567 | this.winning_bpm = (60.0/(winner/10.0)); 568 | } 569 | 570 | // find the overall winner so far 571 | for (var contest_i in this.bpm_contest_lo) 572 | { 573 | if (this.win_val_lo < this.bpm_contest_lo[contest_i]) 574 | { 575 | winner_lo = contest_i; 576 | this.win_val_lo = this.bpm_contest_lo[contest_i]; 577 | } 578 | } 579 | 580 | if (winner_lo) 581 | { 582 | this.win_bpm_int_lo = parseInt(winner_lo); 583 | this.winning_bpm_lo = 60.0/winner_lo; 584 | } 585 | 586 | 587 | if (typeof(console)!='undefined' && (this.beat_counter % 4) == 0) console.log("BeatDetektor("+this.BPM_MIN+","+this.BPM_MAX+"): [ Current Estimate: "+winner+" BPM ] [ Time: "+(parseInt(timer_seconds*1000.0)/1000.0)+"s, Quality: "+(parseInt(this.quality_total*1000.0)/1000.0)+", Rank: "+(parseInt(this.win_val*1000.0)/1000.0)+", Jitter: "+(parseInt(this.bpm_offset*1000000.0)/1000000.0)+" ]"); 588 | } 589 | 590 | } 591 | 592 | // Sample Modules 593 | BeatDetektor.modules = new Object(); 594 | BeatDetektor.modules.vis = new Object(); 595 | 596 | // simple bass kick visualizer assistant module 597 | BeatDetektor.modules.vis.BassKick = function() 598 | { 599 | this.is_kick = false; 600 | } 601 | 602 | BeatDetektor.modules.vis.BassKick.prototype.process = function(det) 603 | { 604 | this.is_kick = ((det.detection[0] && det.detection[1]) || (det.ma_freq_range[0]/det.maa_freq_range[0])>1.4); 605 | } 606 | 607 | BeatDetektor.modules.vis.BassKick.prototype.isKick = function() 608 | { 609 | return this.is_kick; 610 | } 611 | 612 | 613 | // simple vu spectrum visualizer assistant module 614 | BeatDetektor.modules.vis.VU = function() 615 | { 616 | this.vu_levels = new Array(); 617 | } 618 | 619 | BeatDetektor.modules.vis.VU.prototype.process = function(det,lus) 620 | { 621 | var i,det_val,det_max = 0.0; 622 | if (typeof(lus)=='undefined') lus = det.last_update; 623 | 624 | for (i = 0; i < det.config.BD_DETECTION_RANGES; i++) 625 | { 626 | det_val = (det.ma_freq_range[i]/det.maa_freq_range[i]); 627 | if (det_val > det_max) det_max = det_val; 628 | } 629 | 630 | if (det_max <= 0) det_max = 1.0; 631 | 632 | for (i = 0; i < det.config.BD_DETECTION_RANGES; i++) 633 | { 634 | det_val = (det.ma_freq_range[i]/det.maa_freq_range[i]); //fabs(fftData[i*2]/2.0); 635 | 636 | if (det_val != det_val) det_val = 0; 637 | 638 | //&& (det_val > this.vu_levels[i]) 639 | if (det_val>1.0) 640 | { 641 | det_val -= 1.0; 642 | if (det_val>1.0) det_val = 1.0; 643 | 644 | if (det_val > this.vu_levels[i]) 645 | this.vu_levels[i] = det_val; 646 | else if (det.current_bpm) this.vu_levels[i] -= (this.vu_levels[i]-det_val)*lus*(1.0/det.current_bpm)*3.0; 647 | } 648 | else 649 | { 650 | if (det.current_bpm) this.vu_levels[i] -= (lus/det.current_bpm)*2.0; 651 | } 652 | 653 | if (this.vu_levels[i] < 0 || this.vu_levels[i] != this.vu_levels[i]) this.vu_levels[i] = 0; 654 | } 655 | } 656 | 657 | 658 | // returns vu level for BD_DETECTION_RANGES range[x] 659 | BeatDetektor.modules.vis.VU.prototype.getLevel = function(x) 660 | { 661 | return this.vu_levels[x]; 662 | } -------------------------------------------------------------------------------- /audio_scripts/dsp.js: -------------------------------------------------------------------------------- 1 | /* 2 | * DSP.js - a comprehensive digital signal processing library for javascript 3 | * 4 | * Created by Corban Brook on 2010-01-01. 5 | * Copyright 2010 Corban Brook. All rights reserved. 6 | * 7 | */ 8 | 9 | // Setup arrays for platforms which do not support byte arrays 10 | Float32Array = (typeof Float32Array === 'undefined') ? Array : Float32Array; 11 | Float64Array = (typeof Float64Array === 'undefined') ? Array : Float64Array; 12 | Uint32Array = (typeof Uint32Array === 'undefined') ? Array : Uint32Array; 13 | 14 | //////////////////////////////////////////////////////////////////////////////// 15 | // CONSTANTS // 16 | //////////////////////////////////////////////////////////////////////////////// 17 | 18 | /** 19 | * DSP is an object which contains general purpose utility functions and constants 20 | */ 21 | DSP = { 22 | // Channels 23 | LEFT: 0, 24 | RIGHT: 1, 25 | MIX: 2, 26 | 27 | // Waveforms 28 | SINE: 1, 29 | TRIANGLE: 2, 30 | SAW: 3, 31 | SQUARE: 4, 32 | 33 | // Filters 34 | LOWPASS: 0, 35 | HIGHPASS: 1, 36 | BANDPASS: 2, 37 | NOTCH: 3, 38 | 39 | // Window functions 40 | BARTLETT: 1, 41 | BARTLETTHANN: 2, 42 | BLACKMAN: 3, 43 | COSINE: 4, 44 | GAUSS: 5, 45 | HAMMING: 6, 46 | HANN: 7, 47 | LANCZOS: 8, 48 | RECTANGULAR: 9, 49 | TRIANGULAR: 10, 50 | 51 | // Loop modes 52 | OFF: 0, 53 | FW: 1, 54 | BW: 2, 55 | FWBW: 3, 56 | 57 | // Math 58 | TWO_PI: 2*Math.PI 59 | }; 60 | 61 | //////////////////////////////////////////////////////////////////////////////// 62 | // DSP UTILITY FUNCTIONS // 63 | //////////////////////////////////////////////////////////////////////////////// 64 | 65 | /** 66 | * Inverts the phase of a signal 67 | * 68 | * @param {Array} buffer A sample buffer 69 | * 70 | * @returns The inverted sample buffer 71 | */ 72 | DSP.invert = function(buffer) { 73 | for ( var i = 0, len = buffer.length; i < len; i++ ) { 74 | buffer[i] *= -1; 75 | } 76 | 77 | return buffer; 78 | }; 79 | 80 | /** 81 | * Converts split-stereo (dual mono) sample buffers into a stereo interleaved sample buffer 82 | * 83 | * @param {Array} left A sample buffer 84 | * @param {Array} right A sample buffer 85 | * 86 | * @returns The stereo interleaved buffer 87 | */ 88 | DSP.interleave = function(left, right) { 89 | if ( left.length !== right.length ) { 90 | throw "Can not interleave. Channel lengths differ."; 91 | } 92 | 93 | var stereoInterleaved = new Float32Array(left.length * 2); 94 | 95 | for (var i = 0, len = left.length; i < len; i++ ) { 96 | stereoInterleaved[2*i] = left[i]; 97 | stereoInterleaved[2*i+1] = right[i]; 98 | } 99 | 100 | return stereoInterleaved; 101 | }; 102 | 103 | /** 104 | * Converts a stereo-interleaved sample buffer into split-stereo (dual mono) sample buffers 105 | * 106 | * @param {Array} buffer A stereo-interleaved sample buffer 107 | * 108 | * @returns an Array containing left and right channels 109 | */ 110 | DSP.deinterleave = function(buffer) { 111 | var left = new Float32Array(buffer.length/2); 112 | var right = new Float32Array(buffer.length/2); 113 | var mix = new Float32Array(buffer.length/2); 114 | 115 | for (var i = 0, len = buffer.length/2; i < len; i ++ ) { 116 | left[i] = buffer[2*i]; 117 | right[i] = buffer[2*i+1]; 118 | mix[i] = (left[i] + right[i]) / 2; 119 | } 120 | 121 | return [left, right, mix]; 122 | }; 123 | 124 | /** 125 | * Separates a channel from a stereo-interleaved sample buffer 126 | * 127 | * @param {Array} buffer A stereo-interleaved sample buffer 128 | * @param {Number} channel A channel constant (LEFT, RIGHT, MIX) 129 | * 130 | * @returns an Array containing a signal mono sample buffer 131 | */ 132 | DSP.getChannel = function(channel, buffer) { 133 | return DSP.deinterleave(buffer)[channel]; 134 | }; 135 | 136 | /** 137 | * DFT is a class for calculating the Discrete Fourier Transform of a signal. 138 | * 139 | * @param {Number} bufferSize The size of the sample buffer to be computed 140 | * @param {Number} sampleRate The sampleRate of the buffer (eg. 44100) 141 | * 142 | * @constructor 143 | */ 144 | DFT = function(bufferSize, sampleRate) { 145 | this.bufferSize = bufferSize; 146 | this.sampleRate = sampleRate; 147 | 148 | var N = bufferSize/2 * bufferSize; 149 | 150 | this.sinTable = new Float32Array(N); 151 | this.cosTable = new Float32Array(N); 152 | 153 | for ( var i = 0; i < N; i++ ) { 154 | this.sinTable[i] = Math.sin(i * DSP.TWO_PI / bufferSize); 155 | this.cosTable[i] = Math.cos(i * DSP.TWO_PI / bufferSize); 156 | } 157 | 158 | this.spectrum = new Float32Array(bufferSize/2); 159 | this.complexValues = new Float32Array(bufferSize/2); 160 | }; 161 | 162 | /** 163 | * Performs a forward tranform on the sample buffer. 164 | * Converts a time domain signal to frequency domain spectra. 165 | * 166 | * @param {Array} buffer The sample buffer 167 | * 168 | * @returns The frequency spectrum array 169 | */ 170 | DFT.prototype.forward = function(buffer) { 171 | var real, imag; 172 | 173 | for ( var k = 0; k < this.bufferSize/2; k++ ) { 174 | real = 0.0; 175 | imag = 0.0; 176 | 177 | for ( var n = 0; n < buffer.length; n++ ) { 178 | real += this.cosTable[k*n] * signal[n]; 179 | imag += this.sinTable[k*n] * signal[n]; 180 | } 181 | 182 | this.complexValues[k] = {real: real, imag: imag}; 183 | } 184 | 185 | for ( var i = 0; i < this.bufferSize/2; i++ ) { 186 | this.spectrum[i] = 2 * Math.sqrt(Math.pow(this.complexValues[i].real, 2) + Math.pow(this.complexValues[i].imag, 2)) / this.bufferSize; 187 | } 188 | 189 | return this.spectrum; 190 | }; 191 | 192 | 193 | /** 194 | * FFT is a class for calculating the Discrete Fourier Transform of a signal 195 | * with the Fast Fourier Transform algorithm. 196 | * 197 | * @param {Number} bufferSize The size of the sample buffer to be computed. Must be power of 2 198 | * @param {Number} sampleRate The sampleRate of the buffer (eg. 44100) 199 | * 200 | * @constructor 201 | */ 202 | FFT = function(bufferSize, sampleRate) { 203 | this.bufferSize = bufferSize; 204 | this.sampleRate = sampleRate; 205 | this.spectrum = new Float32Array(bufferSize/2); 206 | this.real = new Float32Array(bufferSize); 207 | this.imag = new Float32Array(bufferSize); 208 | 209 | this.reverseTable = new Uint32Array(bufferSize); 210 | 211 | var limit = 1; 212 | var bit = bufferSize >> 1; 213 | 214 | while ( limit < bufferSize ) { 215 | for ( var i = 0; i < limit; i++ ) { 216 | this.reverseTable[i + limit] = this.reverseTable[i] + bit; 217 | } 218 | 219 | limit = limit << 1; 220 | bit = bit >> 1; 221 | } 222 | 223 | this.sinTable = new Float32Array(bufferSize); 224 | this.cosTable = new Float32Array(bufferSize); 225 | 226 | for ( var i = 0; i < bufferSize; i++ ) { 227 | this.sinTable[i] = Math.sin(-Math.PI/i); 228 | this.cosTable[i] = Math.cos(-Math.PI/i); 229 | } 230 | }; 231 | 232 | /** 233 | * Performs a forward tranform on the sample buffer. 234 | * Converts a time domain signal to frequency domain spectra. 235 | * 236 | * @param {Array} buffer The sample buffer. Buffer Length must be power of 2 237 | * 238 | * @returns The frequency spectrum array 239 | */ 240 | FFT.prototype.forward = function(buffer) { 241 | // Locally scope variables for speed up 242 | var bufferSize = this.bufferSize, 243 | cosTable = this.cosTable, 244 | sinTable = this.sinTable, 245 | reverseTable = this.reverseTable, 246 | real = this.real, 247 | imag = this.imag, 248 | spectrum = this.spectrum; 249 | 250 | if ( bufferSize % 2 !== 0 ) { throw "Invalid buffer size, must be a power of 2."; } 251 | if ( bufferSize !== buffer.length ) { throw "Supplied buffer is not the same size as defined FFT. FFT Size: " + bufferSize + " Buffer Size: " + buffer.length; } 252 | 253 | for ( var i = 0; i < bufferSize; i++ ) { 254 | real[i] = buffer[reverseTable[i]]; 255 | imag[i] = 0; 256 | } 257 | 258 | var halfSize = 1, 259 | phaseShiftStepReal, 260 | phaseShiftStepImag, 261 | currentPhaseShiftReal, 262 | currentPhaseShiftImag, 263 | off, 264 | tr, 265 | ti, 266 | tmpReal, 267 | i; 268 | 269 | while ( halfSize < bufferSize ) { 270 | phaseShiftStepReal = cosTable[halfSize]; 271 | phaseShiftStepImag = sinTable[halfSize]; 272 | currentPhaseShiftReal = 1; 273 | currentPhaseShiftImag = 0; 274 | 275 | for ( var fftStep = 0; fftStep < halfSize; fftStep++ ) { 276 | i = fftStep; 277 | 278 | while ( i < bufferSize ) { 279 | off = i + halfSize; 280 | tr = (currentPhaseShiftReal * real[off]) - (currentPhaseShiftImag * imag[off]); 281 | ti = (currentPhaseShiftReal * imag[off]) + (currentPhaseShiftImag * real[off]); 282 | 283 | real[off] = real[i] - tr; 284 | imag[off] = imag[i] - ti; 285 | real[i] += tr; 286 | imag[i] += ti; 287 | 288 | i += halfSize << 1; 289 | } 290 | 291 | tmpReal = currentPhaseShiftReal; 292 | currentPhaseShiftReal = (tmpReal * phaseShiftStepReal) - (currentPhaseShiftImag * phaseShiftStepImag); 293 | currentPhaseShiftImag = (tmpReal * phaseShiftStepImag) + (currentPhaseShiftImag * phaseShiftStepReal); 294 | } 295 | 296 | halfSize = halfSize << 1; 297 | } 298 | 299 | i = bufferSize/2; 300 | while(i--) { 301 | spectrum[i] = 2 * Math.sqrt(real[i] * real[i] + imag[i] * imag[i]) / bufferSize; 302 | } 303 | }; 304 | 305 | Sampler = function Sampler(file, bufferSize, sampleRate, playStart, playEnd, loopStart, loopEnd, loopMode) { 306 | this.file = file; 307 | this.bufferSize = bufferSize; 308 | this.sampleRate = sampleRate; 309 | this.playStart = playStart || 0; // 0% 310 | this.playEnd = playEnd || 1; // 100% 311 | this.loopStart = loopStart || 0; 312 | this.loopEnd = loopEnd || 1; 313 | this.loopMode = loopMode || DSP.OFF; 314 | this.loaded = false; 315 | this.samples = []; 316 | this.signal = new Float32Array(bufferSize); 317 | this.frameCount = 0; 318 | this.envelope = null; 319 | this.amplitude = 1; 320 | this.rootFrequency = 110; // A2 110 321 | this.frequency = 550; 322 | this.step = this.frequency / this.rootFrequency; 323 | this.duration = 0; 324 | this.samplesProcessed = 0; 325 | this.playhead = 0; 326 | 327 | var audio = new Audio(); 328 | var self = this; 329 | 330 | this.loadSamples = function(event) { 331 | var buffer = DSP.getChannel(DSP.MIX, event.mozFrameBuffer); 332 | for ( var i = 0; i < buffer.length; i++) { 333 | self.samples.push(buffer[i]); 334 | } 335 | }; 336 | 337 | this.loadComplete = function() { 338 | // convert flexible js array into a fast typed array 339 | self.samples = new Float32Array(self.samples); 340 | self.loaded = true; 341 | }; 342 | 343 | this.loadMetaData = function() { 344 | self.duration = audio.duration; 345 | }; 346 | 347 | audio.src = file; 348 | audio.addEventListener("audiowritten", this.loadSamples, false); 349 | audio.addEventListener("ended", this.loadComplete, false); 350 | audio.addEventListener("loadedmetadata", this.loadMetaData, false) 351 | audio.muted = true; 352 | audio.play(); 353 | }; 354 | 355 | Sampler.prototype.applyEnvelope = function() { 356 | this.envelope.process(this.signal); 357 | return this.signal; 358 | }; 359 | 360 | Sampler.prototype.generate = function() { 361 | var frameOffset = this.frameCount * this.bufferSize; 362 | 363 | var loopWidth = this.playEnd * this.samples.length - this.playStart * this.samples.length; 364 | var playStartSamples = this.playStart * this.samples.length; // ie 0.5 -> 50% of the length 365 | var playEndSamples = this.playEnd * this.samples.length; // ie 0.5 -> 50% of the length 366 | var offset; 367 | 368 | for ( var i = 0; i < this.bufferSize; i++ ) { 369 | switch (this.loopMode) { 370 | case DSP.OFF: 371 | this.playhead = Math.round(this.samplesProcessed * this.step + playStartSamples); 372 | if (this.playhead < (this.playEnd * this.samples.length) ) { 373 | this.signal[i] = this.samples[this.playhead] * this.amplitude; 374 | } else { 375 | this.signal[i] = 0; 376 | } 377 | break; 378 | 379 | case DSP.FW: 380 | this.playhead = Math.round((this.samplesProcessed * this.step) % loopWidth + playStartSamples); 381 | if (this.playhead < (this.playEnd * this.samples.length) ) { 382 | this.signal[i] = this.samples[this.playhead] * this.amplitude; 383 | } 384 | break; 385 | 386 | case DSP.BW: 387 | this.playhead = playEndSamples - Math.round((this.samplesProcessed * this.step) % loopWidth); 388 | if (this.playhead < (this.playEnd * this.samples.length) ) { 389 | this.signal[i] = this.samples[this.playhead] * this.amplitude; 390 | } 391 | break; 392 | 393 | case DSP.FWBW: 394 | if ( Math.floor(this.samplesProcessed * this.step / loopWidth) % 2 == 0 ) { 395 | this.playhead = Math.round((this.samplesProcessed * this.step) % loopWidth + playStartSamples); 396 | } else { 397 | this.playhead = playEndSamples - Math.round((this.samplesProcessed * this.step) % loopWidth); 398 | } 399 | if (this.playhead < (this.playEnd * this.samples.length) ) { 400 | this.signal[i] = this.samples[this.playhead] * this.amplitude; 401 | } 402 | break; 403 | } 404 | this.samplesProcessed++; 405 | } 406 | 407 | this.frameCount++; 408 | 409 | return this.signal; 410 | }; 411 | 412 | Sampler.prototype.setFreq = function(frequency) { 413 | this.frequency = frequency; 414 | this.step = this.frequency / this.rootFrequency; 415 | }; 416 | 417 | Sampler.prototype.reset = function() { 418 | this.samplesProcessed = 0; 419 | this.playhead = 0; 420 | }; 421 | 422 | /** 423 | * Oscillator class for generating and modifying signals 424 | * 425 | * @param {Number} type A waveform constant (eg. DSP.SINE) 426 | * @param {Number} frequency Initial frequency of the signal 427 | * @param {Number} amplitude Initial amplitude of the signal 428 | * @param {Number} bufferSize Size of the sample buffer to generate 429 | * @param {Number} sampleRate The sample rate of the signal 430 | * 431 | * @contructor 432 | */ 433 | Oscillator = function Oscillator(type, frequency, amplitude, bufferSize, sampleRate) { 434 | this.frequency = frequency; 435 | this.amplitude = amplitude; 436 | this.bufferSize = bufferSize; 437 | this.sampleRate = sampleRate; 438 | //this.pulseWidth = pulseWidth; 439 | this.frameCount = 0; 440 | 441 | this.waveTableLength = 2048; 442 | 443 | this.cyclesPerSample = frequency / sampleRate; 444 | 445 | this.signal = new Float32Array(bufferSize); 446 | this.envelope = null; 447 | 448 | 449 | switch(parseInt(type)) { 450 | case DSP.TRIANGLE: 451 | this.func = Oscillator.Triangle; 452 | break; 453 | 454 | case DSP.SAW: 455 | this.func = Oscillator.Saw; 456 | break; 457 | 458 | case DSP.SQUARE: 459 | this.func = Oscillator.Square; 460 | break; 461 | 462 | case DSP.SINE: 463 | default: 464 | this.func = Oscillator.Sine; 465 | break; 466 | } 467 | 468 | this.generateWaveTable = function() { 469 | Oscillator.waveTable[this.func] = new Float32Array(2048); 470 | var waveTableTime = this.waveTableLength / this.sampleRate; 471 | var waveTableHz = 1 / waveTableTime; 472 | 473 | for (var i = 0; i < this.waveTableLength; i++) { 474 | Oscillator.waveTable[this.func][i] = this.func(i * waveTableHz/this.sampleRate); 475 | } 476 | }; 477 | 478 | if ( typeof Oscillator.waveTable === 'undefined' ) { 479 | Oscillator.waveTable = {}; 480 | } 481 | 482 | if ( typeof Oscillator.waveTable[this.func] === 'undefined' ) { 483 | this.generateWaveTable(); 484 | } 485 | 486 | this.waveTable = Oscillator.waveTable[this.func]; 487 | }; 488 | 489 | /** 490 | * Set the amplitude of the signal 491 | * 492 | * @param {Number} amplitude The amplitude of the signal (between 0 and 1) 493 | */ 494 | Oscillator.prototype.setAmp = function(amplitude) { 495 | if (amplitude >= 0 && amplitude <= 1) { 496 | this.amplitude = amplitude; 497 | } else { 498 | throw "Amplitude out of range (0..1)."; 499 | } 500 | }; 501 | 502 | /** 503 | * Set the frequency of the signal 504 | * 505 | * @param {Number} frequency The frequency of the signal 506 | */ 507 | Oscillator.prototype.setFreq = function(frequency) { 508 | this.frequency = frequency; 509 | this.cyclesPerSample = frequency / this.sampleRate; 510 | }; 511 | 512 | // Add an oscillator 513 | Oscillator.prototype.add = function(oscillator) { 514 | for ( var i = 0; i < this.bufferSize; i++ ) { 515 | //this.signal[i] += oscillator.valueAt(i); 516 | this.signal[i] += oscillator.signal[i]; 517 | } 518 | 519 | return this.signal; 520 | }; 521 | 522 | // Add a signal to the current generated osc signal 523 | Oscillator.prototype.addSignal = function(signal) { 524 | for ( var i = 0; i < signal.length; i++ ) { 525 | if ( i >= this.bufferSize ) { 526 | break; 527 | } 528 | this.signal[i] += signal[i]; 529 | 530 | /* 531 | // Constrain amplitude 532 | if ( this.signal[i] > 1 ) { 533 | this.signal[i] = 1; 534 | } else if ( this.signal[i] < -1 ) { 535 | this.signal[i] = -1; 536 | } 537 | */ 538 | } 539 | return this.signal; 540 | }; 541 | 542 | // Add an envelope to the oscillator 543 | Oscillator.prototype.addEnvelope = function(envelope) { 544 | this.envelope = envelope; 545 | }; 546 | 547 | Oscillator.prototype.applyEnvelope = function() { 548 | this.envelope.process(this.signal); 549 | }; 550 | 551 | Oscillator.prototype.valueAt = function(offset) { 552 | return this.waveTable[offset % this.waveTableLength]; 553 | }; 554 | 555 | Oscillator.prototype.generate = function() { 556 | var frameOffset = this.frameCount * this.bufferSize; 557 | var step = this.waveTableLength * this.frequency / this.sampleRate; 558 | var offset; 559 | 560 | for ( var i = 0; i < this.bufferSize; i++ ) { 561 | //var step = (frameOffset + i) * this.cyclesPerSample % 1; 562 | //this.signal[i] = this.func(step) * this.amplitude; 563 | //this.signal[i] = this.valueAt(Math.round((frameOffset + i) * step)) * this.amplitude; 564 | offset = Math.round((frameOffset + i) * step); 565 | this.signal[i] = this.waveTable[offset % this.waveTableLength] * this.amplitude; 566 | } 567 | 568 | this.frameCount++; 569 | 570 | return this.signal; 571 | }; 572 | 573 | Oscillator.Sine = function(step) { 574 | return Math.sin(DSP.TWO_PI * step); 575 | }; 576 | 577 | Oscillator.Square = function(step) { 578 | return step < 0.5 ? 1 : -1; 579 | }; 580 | 581 | Oscillator.Saw = function(step) { 582 | return 2 * (step - Math.round(step)); 583 | }; 584 | 585 | Oscillator.Triangle = function(step) { 586 | return 1 - 4 * Math.abs(Math.round(step) - step); 587 | }; 588 | 589 | Oscillator.Pulse = function(step) { 590 | // stub 591 | }; 592 | 593 | ADSR = function(attackLength, decayLength, sustainLevel, sustainLength, releaseLength, sampleRate) { 594 | this.sampleRate = sampleRate; 595 | // Length in seconds 596 | this.attackLength = attackLength; 597 | this.decayLength = decayLength; 598 | this.sustainLevel = sustainLevel; 599 | this.sustainLength = sustainLength; 600 | this.releaseLength = releaseLength; 601 | this.sampleRate = sampleRate; 602 | 603 | // Length in samples 604 | this.attackSamples = attackLength * sampleRate; 605 | this.decaySamples = decayLength * sampleRate; 606 | this.sustainSamples = sustainLength * sampleRate; 607 | this.releaseSamples = releaseLength * sampleRate; 608 | 609 | // Updates the envelope sample positions 610 | this.update = function() { 611 | this.attack = this.attackSamples; 612 | this.decay = this.attack + this.decaySamples; 613 | this.sustain = this.decay + this.sustainSamples; 614 | this.release = this.sustain + this.releaseSamples; 615 | }; 616 | 617 | this.update(); 618 | 619 | this.samplesProcessed = 0; 620 | }; 621 | 622 | 623 | ADSR.prototype.noteOn = function() { 624 | this.samplesProcessed = 0; 625 | this.sustainSamples = this.sustainLength * this.sampleRate; 626 | this.update(); 627 | }; 628 | 629 | // Send a note off when using a sustain of infinity to let the envelope enter the release phase 630 | ADSR.prototype.noteOff = function() { 631 | this.sustainSamples = this.samplesProcessed - this.decaySamples; 632 | this.update(); 633 | }; 634 | 635 | ADSR.prototype.processSample = function(sample) { 636 | var amplitude = 0; 637 | 638 | if ( this.samplesProcessed <= this.attack ) { 639 | amplitude = 0 + (1 - 0) * ((this.samplesProcessed - 0) / (this.attack - 0)); 640 | } else if ( this.samplesProcessed > this.attack && this.samplesProcessed <= this.decay ) { 641 | amplitude = 1 + (this.sustainLevel - 1) * ((this.samplesProcessed - this.attack) / (this.decay - this.attack)); 642 | } else if ( this.samplesProcessed > this.decay && this.samplesProcessed <= this.sustain ) { 643 | amplitude = this.sustainLevel; 644 | } else if ( this.samplesProcessed > this.sustain && this.samplesProcessed <= this.release ) { 645 | amplitude = this.sustainLevel + (0 - this.sustainLevel) * ((this.samplesProcessed - this.sustain) / (this.release - this.sustain)); 646 | } 647 | 648 | return sample * amplitude; 649 | }; 650 | 651 | ADSR.prototype.value = function() { 652 | var amplitude = 0; 653 | 654 | if ( this.samplesProcessed <= this.attack ) { 655 | amplitude = 0 + (1 - 0) * ((this.samplesProcessed - 0) / (this.attack - 0)); 656 | } else if ( this.samplesProcessed > this.attack && this.samplesProcessed <= this.decay ) { 657 | amplitude = 1 + (this.sustainLevel - 1) * ((this.samplesProcessed - this.attack) / (this.decay - this.attack)); 658 | } else if ( this.samplesProcessed > this.decay && this.samplesProcessed <= this.sustain ) { 659 | amplitude = this.sustainLevel; 660 | } else if ( this.samplesProcessed > this.sustain && this.samplesProcessed <= this.release ) { 661 | amplitude = this.sustainLevel + (0 - this.sustainLevel) * ((this.samplesProcessed - this.sustain) / (this.release - this.sustain)); 662 | } 663 | 664 | return amplitude; 665 | }; 666 | 667 | ADSR.prototype.process = function(buffer) { 668 | for ( var i = 0; i < buffer.length; i++ ) { 669 | buffer[i] *= this.value(); 670 | 671 | this.samplesProcessed++; 672 | } 673 | 674 | return buffer; 675 | }; 676 | 677 | 678 | ADSR.prototype.isActive = function() { 679 | if ( this.samplesProcessed > this.release || this.samplesProcessed === -1 ) { 680 | return false; 681 | } else { 682 | return true; 683 | } 684 | }; 685 | 686 | ADSR.prototype.disable = function() { 687 | this.samplesProcessed = -1; 688 | }; 689 | 690 | IIRFilter = function(type, cutoff, resonance, sampleRate) { 691 | this.sampleRate = sampleRate; 692 | this.cutoff = cutoff; 693 | this.resonance = resonance; 694 | 695 | switch(type) { 696 | case DSP.LOWPASS: 697 | case DSP.LP12: 698 | this.func = new IIRFilter.LP12(cutoff, resonance, sampleRate); 699 | break; 700 | } 701 | } 702 | 703 | IIRFilter.prototype.set = function(cutoff, resonance) { 704 | this.func.calcCoeff(cutoff, resonance); 705 | } 706 | 707 | IIRFilter.prototype.process = function(buffer) { 708 | this.func.process(buffer); 709 | } 710 | 711 | // Add an envelope to the filter 712 | IIRFilter.prototype.addEnvelope = function(envelope) { 713 | if ( envelope instanceof ADSR ) { 714 | this.func.addEnvelope(envelope); 715 | } else { 716 | throw "Not an envelope."; 717 | } 718 | }; 719 | 720 | IIRFilter.LP12 = function(cutoff, resonance, sampleRate) { 721 | this.sampleRate = sampleRate; 722 | this.vibraPos = 0; 723 | this.vibraSpeed = 0; 724 | this.envelope = false; 725 | 726 | this.calcCoeff = function(cutoff, resonance) { 727 | this.w = 2.0 * Math.PI * cutoff / this.sampleRate; 728 | this.q = 1.0 - this.w / (2.0 * (resonance + 0.5 / (1.0 + this.w)) + this.w - 2.0); 729 | this.r = this.q * this.q; 730 | this.c = this.r + 1.0 - 2.0 * Math.cos(this.w) * this.q; 731 | 732 | this.cutoff = cutoff; 733 | this.resonance = resonance; 734 | }; 735 | 736 | this.calcCoeff(cutoff, resonance); 737 | 738 | this.process = function(buffer) { 739 | for ( var i = 0; i < buffer.length; i++ ) { 740 | this.vibraSpeed += (buffer[i] - this.vibraPos) * this.c; 741 | this.vibraPos += this.vibraSpeed; 742 | this.vibraSpeed *= this.r; 743 | 744 | /* 745 | var temp = this.vibraPos; 746 | 747 | if ( temp > 1.0 ) { 748 | temp = 1.0; 749 | } else if ( temp < -1.0 ) { 750 | temp = -1.0; 751 | } else if ( temp != temp ) { 752 | temp = 1; 753 | } 754 | 755 | buffer[i] = temp; 756 | */ 757 | 758 | if (this.envelope) { 759 | buffer[i] = (buffer[i] * (1 - this.envelope.value())) + (this.vibraPos * this.envelope.value()); 760 | this.envelope.samplesProcessed++; 761 | } else { 762 | buffer[i] = this.vibraPos; 763 | } 764 | } 765 | } 766 | }; 767 | 768 | IIRFilter.LP12.prototype.addEnvelope = function(envelope) { 769 | this.envelope = envelope; 770 | }; 771 | 772 | IIRFilter2 = function(type, cutoff, resonance, sampleRate) { 773 | this.type = type; 774 | this.cutoff = cutoff; 775 | this.resonance = resonance; 776 | this.sampleRate = sampleRate; 777 | 778 | this.calcCoeff = function(cutoff, resonance) { 779 | this.freq = 2 * Math.sin(Math.PI * Math.min(0.25, cutoff/(this.sampleRate*2))); 780 | this.damp = Math.min(2 * (1 - Math.pow(resonance, 0.25)), Math.min(2, 2/this.freq - this.freq * 0.5)); 781 | }; 782 | 783 | this.calcCoeff(cutoff, resonance); 784 | }; 785 | 786 | IIRFilter2.prototype.process = function(buffer) { 787 | var input, output, lp, hp, bp, br; 788 | 789 | var f = Array(4); 790 | f[0] = 0; // lp 791 | f[1] = 0; // hp 792 | f[2] = 0; // bp 793 | f[3] = 0; // br 794 | 795 | for ( var i = 0; i < buffer.length; i++ ) { 796 | input = buffer[i]; 797 | 798 | // first pass 799 | f[3] = input - this.damp * f[2]; 800 | f[0] = f[0] + this.freq * f[2]; 801 | f[1] = f[3] - f[0]; 802 | f[2] = this.freq * f[1] + f[2]; 803 | output = 0.5 * f[this.type]; 804 | 805 | // second pass 806 | f[3] = input - this.damp * f[2]; 807 | f[0] = f[0] + this.freq * f[2]; 808 | f[1] = f[3] - f[0]; 809 | f[2] = this.freq * f[1] + f[2]; 810 | output += 0.5 * f[this.type]; 811 | 812 | if (this.envelope) { 813 | buffer[i] = (buffer[i] * (1 - this.envelope.value())) + (output * this.envelope.value()); 814 | this.envelope.samplesProcessed++; 815 | } else { 816 | buffer[i] = output; 817 | } 818 | } 819 | }; 820 | 821 | IIRFilter2.prototype.addEnvelope = function(envelope) { 822 | if ( envelope instanceof ADSR ) { 823 | this.envelope = envelope; 824 | } else { 825 | throw "This is not an envelope."; 826 | } 827 | }; 828 | 829 | IIRFilter2.prototype.set = function(cutoff, resonance) { 830 | this.calcCoeff(cutoff, resonance); 831 | }; 832 | 833 | WindowFunction = function(type, alpha) { 834 | this.alpha = alpha; 835 | 836 | switch(type) { 837 | case DSP.BARTLETT: 838 | this.func = WindowFunction.Bartlett; 839 | break; 840 | 841 | case DSP.BARTLETTHANN: 842 | this.func = WindowFunction.BartlettHann; 843 | break; 844 | 845 | case DSP.BLACKMAN: 846 | this.func = WindowFunction.Blackman; 847 | this.alpha = this.alpha || 0.16; 848 | break; 849 | 850 | case DSP.COSINE: 851 | this.func = WindowFunction.Cosine; 852 | break; 853 | 854 | case DSP.GAUSS: 855 | this.func = WindowFunction.Gauss; 856 | this.alpha = this.alpha || 0.25; 857 | break; 858 | 859 | case DSP.HAMMING: 860 | this.func = WindowFunction.Hamming; 861 | break; 862 | 863 | case DSP.HANN: 864 | this.func = WindowFunction.Hann; 865 | break; 866 | 867 | case DSP.LANCZOS: 868 | this.func = WindowFunction.Lanczoz; 869 | break; 870 | 871 | case DSP.RECTANGULAR: 872 | this.func = WindowFunction.Rectangular; 873 | break; 874 | 875 | case DSP.TRIANGULAR: 876 | this.func = WindowFunction.Triangular; 877 | break; 878 | } 879 | }; 880 | 881 | WindowFunction.prototype.process = function(buffer) { 882 | var length = buffer.length; 883 | for ( var i = 0; i < length; i++ ) { 884 | buffer[i] *= this.func(length, i, this.alpha); 885 | } 886 | }; 887 | 888 | WindowFunction.Bartlett = function(length, index) { 889 | return 2 / (length - 1) * ((length - 1) / 2 - Math.abs(index - (length - 1) / 2)); 890 | }; 891 | 892 | WindowFunction.BartlettHann = function(length, index) { 893 | return 0.62 - 0.48 * Math.abs(index / (length - 1) - 0.5) - 0.38 * Math.cos(DSP.TWO_PI * index / (length - 1)); 894 | }; 895 | 896 | WindowFunction.Blackman = function(length, index, alpha) { 897 | var a0 = (1 - alpha) / 2; 898 | var a1 = 0.5; 899 | var a2 = alpha / 2; 900 | 901 | return a0 - a1 * Math.cos(DSP.TWO_PI * index / (length - 1)) + a2 * Math.cos(4 * Math.PI * index / (length - 1)); 902 | }; 903 | 904 | WindowFunction.Cosine = function(length, index) { 905 | return Math.cos(Math.PI * index / (length - 1) - Math.PI / 2); 906 | }; 907 | 908 | WindowFunction.Gauss = function(length, index, alpha) { 909 | return Math.pow(Math.E, -0.5 * Math.pow((index - (length - 1) / 2) / (alpha * (length - 1) / 2), 2)); 910 | }; 911 | 912 | WindowFunction.Hamming = function(length, index) { 913 | return 0.54 - 0.46 * Math.cos(DSP.TWO_PI * index / (length - 1)); 914 | }; 915 | 916 | WindowFunction.Hann = function(length, index) { 917 | return 0.5 * (1 - Math.cos(DSP.TWO_PI * index / (length - 1))); 918 | }; 919 | 920 | WindowFunction.Lanczos = function(length, index) { 921 | var x = 2 * index / (length - 1) - 1; 922 | return Math.sin(Math.PI * x) / (Math.PI * x); 923 | }; 924 | 925 | WindowFunction.Rectangular = function(length, index) { 926 | return 1; 927 | }; 928 | 929 | WindowFunction.Triangular = function(length, index) { 930 | return 2 / length * (length / 2 - Math.abs(index - (length - 1) / 2)); 931 | }; 932 | -------------------------------------------------------------------------------- /audio_scripts/main.js: -------------------------------------------------------------------------------- 1 | var frameSize = 2048, 2 | bufferSize = frameSize / 2, 3 | sampleRate = 44100, 4 | signal = new Float32Array(bufferSize), 5 | fft = new FFT(bufferSize, sampleRate), 6 | bd = new BeatDetektor(60, 90), 7 | vu = new BeatDetektor.modules.vis.VU(), 8 | m_BeatCounter = 0, 9 | m_BeatTimer = 0, 10 | clearClr = [0,0,1], 11 | ftimer = 0, 12 | audio; 13 | 14 | window.addEventListener("load", function() { 15 | audio = document.getElementById("grappes"); 16 | audio.addEventListener('MozAudioAvailable', audioAvailable, false); 17 | }, true); 18 | 19 | function startProcessingAudio() { 20 | Processing.getInstanceById("audiocube").loop(); 21 | audio.play(); 22 | } 23 | 24 | function stopProcessingAudio() { 25 | Processing.getInstanceById("audiocube").noLoop(); 26 | audio.pause(); 27 | } 28 | 29 | function audioAvailable(event) { 30 | var frameBuffer = event.frameBuffer; 31 | timestamp = event.time; 32 | // de-interleave and mix down to mono 33 | signal = DSP.getChannel(DSP.MIX, frameBuffer); 34 | // perform forward transform 35 | fft.forward(signal); 36 | // beat detection 37 | bd.process( timestamp, fft.spectrum ); 38 | ftimer += bd.last_update; 39 | if (ftimer > 1.0/24.0) { 40 | vu.process(bd,ftimer); 41 | ftimer = 0; 42 | } 43 | } 44 | 45 | -------------------------------------------------------------------------------- /demos.css: -------------------------------------------------------------------------------- 1 | /* SOME SPECIFIC LAYOUTS */ 2 | 3 | article, section {display: block;} 4 | 5 | .parser { width: 140px; text-align: center} 6 | .parser > * { vertical-align: middle; display: inline; } 7 | 8 | .canvas2d:after { content: url(imgs/overlay.png)!important; position: absolute; top: 0; left: 0; pointer-events: none; z-index: 65000; background-color: transparent} 9 | .canvas2d canvas { width: 150px; } 10 | 11 | #audiodemo > .demo, 12 | #audiodemo > .demo > canvas { width: 100px; height: 100px; } 13 | 14 | #tagsoup .demo p { text-align: left;} 15 | #tagsoup .demo span { font-family: bebas; } 16 | #tagsoup:hover > .demo { top: 30px!important; } 17 | 18 | #fontfacedemo {width: 170px; float: right; margin-left: 10px;} 19 | #fontfacedemo * { margin: 0; padding: 0; vertical-align: top;} 20 | #fontfacedemo span {font-size: 18px; letter-spacing: -1px; display: inline-block;} 21 | 22 | #radiusdemo { transition-duration: 3s;-moz-transition-duration: 3s;-webkit-transition-duration: 3s; border: 1px solid transparent; max-width: 220px; border-top: 1px solid transparent!important; } 23 | #radiusdemo:hover { border: 1px solid black; border-top: 1px solid black!important; border-radius: 35px;} 24 | 25 | #gradientdemo:hover { 26 | background-image: -moz-radial-gradient(50% 120%, circle, white, rgba(40, 194, 218, 1) 40%, rgba(0, 194, 218, 0) 60%) , url("imgs/texture.png") !important; 27 | background-image: -webkit-radial-gradient(50% 120%, circle, white, rgba(40, 194, 218, 1) 40%, rgba(0, 194, 218, 0) 60%) , url("imgs/texture.png") !important; 28 | } 29 | 30 | #geodemo .demo.showme { top: 40px; } 31 | 32 | #orientationdemo .demo {padding: 10px;} 33 | 34 | .dnding #dnddemo .demo {top: 100px;} 35 | #dnddemo .demo img {width: 200px;} 36 | #dnddemo .demo {text-align: center; padding: 5px;} 37 | 38 | #dragdemo {background-color: #E1E6E9; cursor: pointer;} 39 | #dragdemo .demo {width: 200px;} 40 | 41 | #columndemo {width: 245px!important; padding-right: 10px!important;} 42 | #columndemo > p {-moz-column-count: 2; text-align: left!important;} 43 | 44 | #boxshadowdemo { box-shadow: inset 0px 0px 0px black; transition-duration: 0.4s; -moz-transition-duration: 0.4s; -webkit-transition-duration: 0.4s; -webkit-box-shadow: inset 0px 0px 0px black; } 45 | #boxshadowdemo:hover { box-shadow: inset 0px 0px 100px black; -webkit-box-shadow: inset 0px 0px 100px black; } 46 | 47 | #textshadowdemo > div { padding: 10px; } 48 | #textshadowdemo > .demo {background-color: #E1E6E9!important;} 49 | #textshadowdemo > div > p { color: white; font-family: bebas; font-size: 25px; text-shadow: 0px 0px 4px black; } 50 | 51 | #transformdemo p { transition-duration: 1.4s;-moz-transition-duration: 1.4s;-webkit-transition-duration: 1.4s; } 52 | #transformdemo:hover p { -moz-transform: rotate(900deg) scale(-1, -1); -webkit-transform: rotate(900deg) scale(-1, -1);} 53 | 54 | #transitiondemo { background-color: transparent!important; 55 | border-width: 0!important; 56 | -webkit-box-shadow: none!important; 57 | -moz-box-shadow: none!important; 58 | font-family: ubuntu; 59 | font-size: 60px; 60 | display: block; 61 | text-align: center; 62 | margin: auto; 63 | text-shadow: 0px 0px 5px black; 64 | color: rgba(0, 0, 0, 0); 65 | margin-top: 20px; 66 | -webkit-transition-duration: 1s!important; 67 | -moz-transition-duration: 6s!important; 68 | -moz-transition-timing-function: cubic-bezier(0.05, 1.6, 0.25, 1); 69 | } 70 | #transitiondemo:hover { -webkit-transform: scale(3) rotate(360deg); -moz-transform: scale(3) rotate(360deg); text-shadow: 0px 0px 3px #28C2DA; } 71 | 72 | #formdemo input { display: inline; width: 70px; border: none; font-size: 12px; box-shadow: 0px 0px 5px #28C2DA; } 73 | #formdemo button { box-shadow: 0px 0px 5px #28C2DA; background-color: white; color: #28C2DA; border: none; font-weight: bold; float: right; } 74 | #formdemo em { display: block; margin-top: 20px; vertical-align: middle; } 75 | #formdemo input:last-of-type { width: 90px;} 76 | #formdemo .demo { padding: 3px; width: 160px; font-size: 12px;} 77 | #formdemo:not(.submited) .demo > p {display: none;} 78 | #formdemo:not(.submited) .demo > form {display: inline;} 79 | #formdemo.submited .demo > p {display: block;} 80 | #formdemo.submited .demo > form {display: none;} 81 | 82 | #multiplatform img {width: 150px; display: block; margin-top: 80px;} 83 | 84 | .nogl #webgldemo .demo {display: none;} 85 | #webgldemo canvas { transition-duration: 1s; -webkit-transition-duration: 1s; -moz-transition-duration: 1s; width: 150px; height: 100px; transition-delay: 1s; -webkit-transition-delay: 1s; -moz-transition-delay: 1s; z-index: 10000;} 86 | #webgldemo canvas:hover { transform: scale(2); -webkit-transform: scale(2); -moz-transform: scale(2);} 87 | 88 | #borderimagedemo {transition: none;-webkit-transition: none;-moz-transition: none;} 89 | #borderimagedemo:hover { -webkit-border-image: url(imgs/tv-border.jpg) 25 31 37 31 stretch stretch; -moz-border-image: url(imgs/tv-border.jpg) 25 31 37 31 stretch stretch; border-width: 20px!important; background-color: white!important; color: white; padding: 10px!important; max-height: 210px; } 90 | #borderimagedemo:hover p {margin-top: 40px;} 91 | #borderimagedemo:hover h1 {top: 20px;} 92 | 93 | #mozelementdemo { 94 | background-image: -moz-element(#overview); 95 | background-size: 100% 30%; 96 | background-repeat: no-repeat; 97 | background-position: 0 170px; 98 | } 99 | 100 | #ffsdemo:hover .demo {top: 30px!important;} 101 | #ffsdemo .demo { font-family: megalopolis; font-size: 15px; text-align: center;} 102 | .dlig { -moz-font-feature-settings: "dlig=1"; } 103 | .dlig-ss01 { -moz-font-feature-settings: "dlig=1,ss01=1"; } 104 | .dlig-ss02 { -moz-font-feature-settings: "dlig=1,ss02=1,case=1"; } 105 | .ss04 { -moz-font-feature-settings: "ss04=1"; } 106 | .ss05 { -moz-font-feature-settings: "ss05=1"; } 107 | .ss06 { -moz-font-feature-settings: "ss06=1"; letter-spacing: -1px; } 108 | .salt { -moz-font-feature-settings: "salt=1"; } 109 | 110 | /* 111 | * Project: circular player 112 | * http://www.jplayer.org 113 | * Copyright (c) 2011 Happyworm Ltd 114 | * Author: Silvia Benvenuti 115 | * Date: 18th January 2011 116 | * Arwork inspired by: http://forrst.com/posts/Untitled-CJz 117 | */ 118 | .player :focus { border:none; outline:0; } 119 | #jquery_jplayer_1 { width: 0; height: 0; } 120 | .player { width: 200px; height: 200px; background: url("imgs/bgr.jpg") 0 0 no-repeat; margin: 0 auto; padding: 48px; } 121 | #audioplayerdemo > .demo { height: 200px;} 122 | #audioplayerdemo:hover > .demo { top: 30px;} 123 | #jp_interface_1 { width: 100px; height: 100px; overflow: hidden; } 124 | .progress { position: absolute; top: 0; left: 0; width: 104px; height: 104px; background: url("imgs/progress.png") 0 0 no-repeat; clip:rect(0px,52px,104px,0px); -moz-border-radius:52px; -webkit-border-radius:52px; border-radius:52px; -moz-transform:rotate(0); -webkit-transform:rotate(0); -o-transform:rotate(0); transform:rotate(0); /* NO CSS3 FALLBACK (12 steps starting from 1hr filled progress, DEcrease second value by 104px for next step) background: url("graphics/progress_sprite.jpg") 0 0 no-repeat; */ } 125 | /* this is needed when progress is greater than 50% */ .progress.fill { -moz-transform:rotate(0deg); -webkit-transform:rotate(0deg); transform:rotate(0deg); -o-transform:rotate(0deg); } 126 | .hold, .click-control { position:absolute; width:104px; height:104px; } 127 | .click-control:hover { cursor:pointer; } 128 | .hold { clip:rect(0px,104px,104px,52px); } 129 | /* this is needed when progress is greater than 50% */ .hold.gt50 { clip:rect(auto, auto, auto, auto); } 130 | .jp-controls { margin:26px; padding: 0; } 131 | .jp-controls li{ list-style-type:none; display: block; } 132 | .jp-controls li a{ position: relative; display: block; width:50px; height:50px; text-indent:-9999px; z-index:1; } 133 | .jp-controls .jp-play { background: url("imgs/controls.jpg") 0 0 no-repeat; } 134 | .jp-controls .jp-play:hover { background: url("imgs/controls.jpg") -50px 0 no-repeat; cursor:pointer; } 135 | .jp-controls .jp-pause { background: url("imgs/controls.jpg") 0 -50px no-repeat; } 136 | .jp-controls .jp-pause:hover { background: url("imgs/controls.jpg") -50px -50px no-repeat; } 137 | 138 | 139 | 140 | 141 | 142 | -------------------------------------------------------------------------------- /fonts/BebasNeue.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulrouget/html5dashboard/6fe0865bf493597bb3b788fce712c3a9d051ebcb/fonts/BebasNeue.otf -------------------------------------------------------------------------------- /fonts/MEgalopolisExtra.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulrouget/html5dashboard/6fe0865bf493597bb3b788fce712c3a9d051ebcb/fonts/MEgalopolisExtra.woff -------------------------------------------------------------------------------- /fonts/MarketingScript.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulrouget/html5dashboard/6fe0865bf493597bb3b788fce712c3a9d051ebcb/fonts/MarketingScript.ttf -------------------------------------------------------------------------------- /fonts/ubuntu.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulrouget/html5dashboard/6fe0865bf493597bb3b788fce712c3a9d051ebcb/fonts/ubuntu.ttf -------------------------------------------------------------------------------- /imgs/bgr.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulrouget/html5dashboard/6fe0865bf493597bb3b788fce712c3a9d051ebcb/imgs/bgr.jpg -------------------------------------------------------------------------------- /imgs/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulrouget/html5dashboard/6fe0865bf493597bb3b788fce712c3a9d051ebcb/imgs/close.png -------------------------------------------------------------------------------- /imgs/controls.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulrouget/html5dashboard/6fe0865bf493597bb3b788fce712c3a9d051ebcb/imgs/controls.jpg -------------------------------------------------------------------------------- /imgs/dashed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulrouget/html5dashboard/6fe0865bf493597bb3b788fce712c3a9d051ebcb/imgs/dashed.png -------------------------------------------------------------------------------- /imgs/link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulrouget/html5dashboard/6fe0865bf493597bb3b788fce712c3a9d051ebcb/imgs/link.png -------------------------------------------------------------------------------- /imgs/lion.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | 10 | 12 | 14 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | -------------------------------------------------------------------------------- /imgs/overlay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulrouget/html5dashboard/6fe0865bf493597bb3b788fce712c3a9d051ebcb/imgs/overlay.png -------------------------------------------------------------------------------- /imgs/phial.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /imgs/progress.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulrouget/html5dashboard/6fe0865bf493597bb3b788fce712c3a9d051ebcb/imgs/progress.png -------------------------------------------------------------------------------- /imgs/texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulrouget/html5dashboard/6fe0865bf493597bb3b788fce712c3a9d051ebcb/imgs/texture.png -------------------------------------------------------------------------------- /imgs/throbber.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulrouget/html5dashboard/6fe0865bf493597bb3b788fce712c3a9d051ebcb/imgs/throbber.gif -------------------------------------------------------------------------------- /imgs/tv-border.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulrouget/html5dashboard/6fe0865bf493597bb3b788fce712c3a9d051ebcb/imgs/tv-border.jpg -------------------------------------------------------------------------------- /imgs/whiteButton.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulrouget/html5dashboard/6fe0865bf493597bb3b788fce712c3a9d051ebcb/imgs/whiteButton.png -------------------------------------------------------------------------------- /logos/android.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulrouget/html5dashboard/6fe0865bf493597bb3b788fce712c3a9d051ebcb/logos/android.png -------------------------------------------------------------------------------- /logos/apple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulrouget/html5dashboard/6fe0865bf493597bb3b788fce712c3a9d051ebcb/logos/apple.png -------------------------------------------------------------------------------- /logos/linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulrouget/html5dashboard/6fe0865bf493597bb3b788fce712c3a9d051ebcb/logos/linux.png -------------------------------------------------------------------------------- /logos/maemo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulrouget/html5dashboard/6fe0865bf493597bb3b788fce712c3a9d051ebcb/logos/maemo.png -------------------------------------------------------------------------------- /logos/meego.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulrouget/html5dashboard/6fe0865bf493597bb3b788fce712c3a9d051ebcb/logos/meego.png -------------------------------------------------------------------------------- /logos/w3c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulrouget/html5dashboard/6fe0865bf493597bb3b788fce712c3a9d051ebcb/logos/w3c.png -------------------------------------------------------------------------------- /logos/webm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulrouget/html5dashboard/6fe0865bf493597bb3b788fce712c3a9d051ebcb/logos/webm.png -------------------------------------------------------------------------------- /logos/windows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulrouget/html5dashboard/6fe0865bf493597bb3b788fce712c3a9d051ebcb/logos/windows.png -------------------------------------------------------------------------------- /logos/xiph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulrouget/html5dashboard/6fe0865bf493597bb3b788fce712c3a9d051ebcb/logos/xiph.png -------------------------------------------------------------------------------- /media/enfants.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulrouget/html5dashboard/6fe0865bf493597bb3b788fce712c3a9d051ebcb/media/enfants.webm -------------------------------------------------------------------------------- /media/track.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulrouget/html5dashboard/6fe0865bf493597bb3b788fce712c3a9d051ebcb/media/track.ogg -------------------------------------------------------------------------------- /screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulrouget/html5dashboard/6fe0865bf493597bb3b788fce712c3a9d051ebcb/screenshot.jpg -------------------------------------------------------------------------------- /scripts/audioplayer.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function(){ 2 | 3 | var myPlayer = $("#jquery_jplayer_1"); 4 | 5 | myPlayer.jPlayer({ 6 | ready: function () { 7 | $(this).jPlayer("setMedia", { 8 | oga: "media/track.ogg" 9 | }); 10 | }, 11 | swfPath: "js", 12 | supplied: "oga" 13 | }); 14 | 15 | function displayProgress(pc) { 16 | var degs = pc * 3.6+"deg"; 17 | 18 | if (pc <= 50) { 19 | $('#circle-holder').removeClass('gt50'); 20 | $('#progress1').css({rotate: degs}); 21 | $('#progress2').hide(); 22 | } 23 | else if (pc < 100) { 24 | $('#circle-holder').addClass('gt50'); 25 | $('#progress1').css({rotate: '180deg'}); 26 | $('#progress2').show(); 27 | $('#progress2').css({rotate: degs}); 28 | } 29 | } 30 | 31 | myPlayer.bind($.jPlayer.event.timeupdate, function(event) { 32 | var pc = event.jPlayer.status.currentPercentAbsolute; 33 | displayProgress(pc); 34 | }); 35 | 36 | myPlayer.bind($.jPlayer.event.ended, function() { 37 | $('#circle-holder').removeClass('gt50'); 38 | $('#progress1').css({rotate: '0deg'}).show(); 39 | $('#progress2').css({rotate: '0deg'}).hide(); 40 | }); 41 | 42 | $('.click-control').click(function(event) { 43 | var self = $(this); 44 | var x = event.pageX - self.offset().left - self.width()/2; 45 | var y = event.pageY - self.offset().top - self.height()/2; 46 | 47 | var a = Math.atan2(y,x); 48 | if (a > -1*Math.PI && a < -0.5*Math.PI) { 49 | a = 2*Math.PI+a; 50 | } 51 | 52 | // a is now value between -0.5PI and 1.5PI 53 | // ready to be normalized and applied 54 | 55 | var pc = (a + Math.PI/2) / 2*Math.PI * 10; 56 | 57 | myPlayer.jPlayer("playHead", pc).jPlayer("play"); 58 | 59 | return false; 60 | }); 61 | 62 | }); 63 | -------------------------------------------------------------------------------- /scripts/canvas2d.js: -------------------------------------------------------------------------------- 1 | function chain( func ) { return function() { return func.apply( this, arguments )||this; } } 2 | CanvasRenderingContext2D.prototype.set = function( what, to ) { this[what] = to; } 3 | for( chainThat in {set:1,clearRect:1,save:1,translate:1,rotate:1,drawImage:1,scale:1,restore:1,fillRect:1,moveTo:1,lineTo:1,beginPath:1,closePath:1,stroke:1,fill:1,arc:1} ) { CanvasRenderingContext2D.prototype[chainThat] = chain( CanvasRenderingContext2D.prototype[chainThat] ); } 4 | onmousemove = function( event ) { window.mouse = [ (window.event||event).screenX, (window.event||event).screenY ]; } 5 | 6 | window.addEventListener("load", function() { 7 | var canvas = document.getElementById('canvas2d'); 8 | var interval; 9 | 10 | for (var i = 0; i < 60; i++) {tick()}; 11 | 12 | canvas.addEventListener("mouseout", function() { if (interval) clearInterval(interval); }, true); 13 | canvas.addEventListener("mouseover", function() { interval = setInterval(tick, 50 );} , true); 14 | 15 | function tick() { 16 | //By http://www.p01.org/news/ 17 | //From http://www.p01.org/releases/20_lines_twinkle/ 18 | 19 | var now = new Date().getTime()+(window.mouse||[0,0])[0]*2-200; 20 | 21 | var canvas = document.getElementById('canvas2d'); 22 | var ctx = canvas.getContext('2d'); 23 | 24 | ctx.globalCompositeOperation = 'copy'; 25 | ctx.drawImage(canvas, 12+Math.cos(now/806+(window.mouse||[0,0])[0]/128),8+Math.sin(now/551+(window.mouse||[0,0])[1]/128),240-24,160-16,0,0,240,160); 26 | ctx.globalCompositeOperation = 'source-over'; 27 | 28 | ctx.beginPath(); 29 | ctx.fillStyle = "black"; 30 | ctx.arc(120,80,12,0,Math.PI * 2, .628,0); 31 | ctx.closePath(); 32 | ctx.fill(); 33 | 34 | var colors = ["#28C2DA", "white", "black", "#2EC4DD"]; 35 | 36 | for( var j=0; j<10; j++ ) { 37 | var a = j/1.59+((Math.cos(now/1337)+now/4096)%1.2566); 38 | ctx.beginPath(); 39 | ctx.moveTo(120-Math.sin(now/618),80+Math.cos(now/523)); 40 | ctx.arc(120,80,12,a,a+.628,0); 41 | ctx.closePath(); 42 | ctx.fillStyle = colors[Math.round(Math.random() * colors.length)];//'#'+('00'+(Math.random()*0xfff&(j&1?0x17f:0xf31)).toString(16)).slice(-3); 43 | ctx.fill(); 44 | } 45 | } 46 | }, true); 47 | -------------------------------------------------------------------------------- /scripts/classList.js: -------------------------------------------------------------------------------- 1 | /* 2 | * classList.js: Implements a cross-browser element.classList getter. 3 | * 2011-01-24 4 | * 5 | * By Eli Grey, http://eligrey.com 6 | * Public Domain. 7 | * NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. 8 | */ 9 | 10 | if (typeof Element !== "undefined" && !Element.prototype.hasOwnProperty("classList")) { 11 | 12 | (function () { 13 | 14 | var 15 | classListProp = "classList" 16 | , protoProp = "prototype" 17 | , elemCtrProto = Element[protoProp] 18 | , objCtr = Object 19 | strTrim = String[protoProp].trim || function () { 20 | return this.replace(/^\s+|\s+$/g, ""); 21 | } 22 | , arrIndexOf = Array[protoProp].indexOf || function (item) { 23 | for (var i = 0, len = this.length; i < len; i++) { 24 | if (i in this && this[i] === item) { 25 | return i; 26 | } 27 | } 28 | return -1; 29 | } 30 | // Vendors: please allow content code to instantiate DOMExceptions 31 | , DOMEx = function (type, message) { 32 | this.name = type; 33 | this.code = DOMException[type]; 34 | this.message = message; 35 | } 36 | , checkTokenAndGetIndex = function (classList, token) { 37 | if (token === "") { 38 | throw new DOMEx( 39 | "SYNTAX_ERR" 40 | , "An invalid or illegal string was specified" 41 | ); 42 | } 43 | if (/\s/.test(token)) { 44 | throw new DOMEx( 45 | "INVALID_CHARACTER_ERR" 46 | , "String contains an invalid character" 47 | ); 48 | } 49 | return arrIndexOf.call(classList, token); 50 | } 51 | , ClassList = function (elem) { 52 | var 53 | trimmedClasses = strTrim.call(elem.className) 54 | , classes = trimmedClasses ? trimmedClasses.split(/\s+/) : [] 55 | ; 56 | for (var i = 0, len = classes.length; i < len; i++) { 57 | this.push(classes[i]); 58 | } 59 | this._updateClassName = function () { 60 | elem.className = this.toString(); 61 | }; 62 | } 63 | , classListProto = ClassList[protoProp] = [] 64 | , classListGetter = function () { 65 | return new ClassList(this); 66 | } 67 | ; 68 | // Most DOMException implementations don't allow calling DOMException's toString() 69 | // on non-DOMExceptions. Error's toString() is sufficient here. 70 | DOMEx[protoProp] = Error[protoProp]; 71 | classListProto.item = function (i) { 72 | return this[i] || null; 73 | }; 74 | classListProto.contains = function (token) { 75 | token += ""; 76 | return checkTokenAndGetIndex(this, token) !== -1; 77 | }; 78 | classListProto.add = function (token) { 79 | token += ""; 80 | if (checkTokenAndGetIndex(this, token) === -1) { 81 | this.push(token); 82 | this._updateClassName(); 83 | } 84 | }; 85 | classListProto.remove = function (token) { 86 | token += ""; 87 | var index = checkTokenAndGetIndex(this, token); 88 | if (index !== -1) { 89 | this.splice(index, 1); 90 | this._updateClassName(); 91 | } 92 | }; 93 | classListProto.toggle = function (token) { 94 | token += ""; 95 | if (checkTokenAndGetIndex(this, token) === -1) { 96 | this.add(token); 97 | } else { 98 | this.remove(token); 99 | } 100 | }; 101 | classListProto.toString = function () { 102 | return this.join(" "); 103 | }; 104 | 105 | if (objCtr.defineProperty) { 106 | var classListDescriptor = { 107 | get: classListGetter 108 | , enumerable: true 109 | , configurable: true 110 | }; 111 | try { 112 | objCtr.defineProperty(elemCtrProto, classListProp, classListDescriptor); 113 | } catch (ex) { // IE 8 doesn't support enumerable:true 114 | if (ex.number === -0x7FF5EC54) { 115 | classListDescriptor.enumerable = false; 116 | objCtr.defineProperty(elemCtrProto, classListProp, classListDescriptor); 117 | } 118 | } 119 | } else if (objCtr[protoProp].__defineGetter__) { 120 | elemCtrProto.__defineGetter__(classListProp, classListGetter); 121 | } 122 | 123 | }()); 124 | 125 | } 126 | -------------------------------------------------------------------------------- /scripts/dnd.js: -------------------------------------------------------------------------------- 1 | window.addEventListener("load", function() { 2 | 3 | var s = document.querySelector("#dnddemo"); 4 | var initialized = false; 5 | 6 | function dragenter(e) { 7 | document.body.classList.add("dnding"); 8 | e.stopPropagation(); 9 | e.preventDefault(); 10 | } 11 | function dragleave(e) { 12 | e.stopPropagation(); 13 | e.preventDefault(); 14 | } 15 | function dragover(e) { e.stopPropagation(); e.preventDefault(); } 16 | 17 | function filesdropped(e) { 18 | document.body.classList.remove("dnding"); 19 | 20 | e.stopPropagation(); 21 | e.preventDefault(); 22 | 23 | if (e.dataTransfer.files.length == 0) return; 24 | 25 | var file = e.dataTransfer.files[0]; 26 | var reader = new FileReader(); 27 | reader.readAsDataURL(file); 28 | reader.onload = (function(file) { 29 | return function(e) { 30 | var url = e.target.result; 31 | var img = document.querySelector("#dnddemo img"); 32 | img.src = url; 33 | }})(file); 34 | } 35 | window.addEventListener("dragenter", dragenter, true); 36 | window.addEventListener("dragleave", dragleave, true); 37 | window.addEventListener("dragover", dragover, true); 38 | s.addEventListener("drop", filesdropped, true); 39 | }, true); 40 | -------------------------------------------------------------------------------- /scripts/geolocation.js: -------------------------------------------------------------------------------- 1 | function geo_success(position) { 2 | var mapcanvas = document.createElement('div'); 3 | mapcanvas.id = 'mapcanvas'; 4 | mapcanvas.style.height = '200px'; 5 | mapcanvas.style.width = '200px'; 6 | 7 | var demo = document.querySelector('#geodemo .demo'); 8 | demo.appendChild(mapcanvas); 9 | demo.querySelector("button").style.display = "none"; 10 | 11 | var latlng = new google.maps.LatLng(position.coords.latitude, position.coords.longitude); 12 | var myOptions = { 13 | zoom: 15, 14 | center: latlng, 15 | mapTypeControl: false, 16 | navigationControlOptions: {style: google.maps.NavigationControlStyle.SMALL}, 17 | mapTypeId: google.maps.MapTypeId.ROADMAP 18 | }; 19 | var map = new google.maps.Map(mapcanvas, myOptions); 20 | 21 | var marker = new google.maps.Marker({ 22 | position: latlng, 23 | map: map, 24 | title:"You are here!" 25 | }); 26 | } 27 | 28 | function getCoords() { 29 | var demo = document.querySelector('#geodemo .demo'); 30 | demo.classList.add("showme"); 31 | window.addEventListener("click", function() { 32 | demo.classList.remove("showme"); 33 | window.removeEventListener("click", arguments.callee, true); 34 | }, true); 35 | 36 | navigator.geolocation.getCurrentPosition(geo_success, function() {console.log("geo error")}); 37 | } 38 | -------------------------------------------------------------------------------- /scripts/jquery.jplayer.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jPlayer Plugin for jQuery JavaScript Library 3 | * http://www.happyworm.com/jquery/jplayer 4 | * 5 | * Copyright (c) 2009 - 2010 Happyworm Ltd 6 | * Dual licensed under the MIT and GPL licenses. 7 | * - http://www.opensource.org/licenses/mit-license.php 8 | * - http://www.gnu.org/copyleft/gpl.html 9 | * 10 | * Author: Mark J Panaghiston 11 | * Version: 2.0.0 12 | * Date: 20th December 2010 13 | */ 14 | 15 | (function(c,h){c.fn.jPlayer=function(a){var b=typeof a==="string",d=Array.prototype.slice.call(arguments,1),f=this;a=!b&&d.length?c.extend.apply(null,[true,a].concat(d)):a;if(b&&a.charAt(0)==="_")return f;b?this.each(function(){var e=c.data(this,"jPlayer"),g=e&&c.isFunction(e[a])?e[a].apply(e,d):e;if(g!==e&&g!==h){f=g;return false}}):this.each(function(){var e=c.data(this,"jPlayer");if(e){e.option(a||{})._init();e.option(a||{})}else c.data(this,"jPlayer",new c.jPlayer(a,this))});return f};c.jPlayer= 16 | function(a,b){if(arguments.length){this.element=c(b);this.options=c.extend(true,{},this.options,a);var d=this;this.element.bind("remove.jPlayer",function(){d.destroy()});this._init()}};c.jPlayer.event={ready:"jPlayer_ready",resize:"jPlayer_resize",error:"jPlayer_error",warning:"jPlayer_warning",loadstart:"jPlayer_loadstart",progress:"jPlayer_progress",suspend:"jPlayer_suspend",abort:"jPlayer_abort",emptied:"jPlayer_emptied",stalled:"jPlayer_stalled",play:"jPlayer_play",pause:"jPlayer_pause",loadedmetadata:"jPlayer_loadedmetadata", 17 | loadeddata:"jPlayer_loadeddata",waiting:"jPlayer_waiting",playing:"jPlayer_playing",canplay:"jPlayer_canplay",canplaythrough:"jPlayer_canplaythrough",seeking:"jPlayer_seeking",seeked:"jPlayer_seeked",timeupdate:"jPlayer_timeupdate",ended:"jPlayer_ended",ratechange:"jPlayer_ratechange",durationchange:"jPlayer_durationchange",volumechange:"jPlayer_volumechange"};c.jPlayer.htmlEvent=["loadstart","abort","emptied","stalled","loadedmetadata","loadeddata","canplaythrough","ratechange"];c.jPlayer.pause= 18 | function(){c.each(c.jPlayer.prototype.instances,function(a,b){b.data("jPlayer").status.srcSet&&b.jPlayer("pause")})};c.jPlayer.timeFormat={showHour:false,showMin:true,showSec:true,padHour:false,padMin:true,padSec:true,sepHour:":",sepMin:":",sepSec:""};c.jPlayer.convertTime=function(a){a=new Date(a*1E3);var b=a.getUTCHours(),d=a.getUTCMinutes();a=a.getUTCSeconds();b=c.jPlayer.timeFormat.padHour&&b<10?"0"+b:b;d=c.jPlayer.timeFormat.padMin&&d<10?"0"+d:d;a=c.jPlayer.timeFormat.padSec&&a<10?"0"+a:a;return(c.jPlayer.timeFormat.showHour? 19 | b+c.jPlayer.timeFormat.sepHour:"")+(c.jPlayer.timeFormat.showMin?d+c.jPlayer.timeFormat.sepMin:"")+(c.jPlayer.timeFormat.showSec?a+c.jPlayer.timeFormat.sepSec:"")};c.jPlayer.uaMatch=function(a){a=a.toLowerCase();var b=/(opera)(?:.*version)?[ \/]([\w.]+)/,d=/(msie) ([\w.]+)/,f=/(mozilla)(?:.*? rv:([\w.]+))?/;a=/(webkit)[ \/]([\w.]+)/.exec(a)||b.exec(a)||d.exec(a)||a.indexOf("compatible")<0&&f.exec(a)||[];return{browser:a[1]||"",version:a[2]||"0"}};c.jPlayer.browser={};var m=c.jPlayer.uaMatch(navigator.userAgent); 20 | if(m.browser){c.jPlayer.browser[m.browser]=true;c.jPlayer.browser.version=m.version}c.jPlayer.prototype={count:0,version:{script:"2.0.0",needFlash:"2.0.0",flash:"unknown"},options:{swfPath:"js",solution:"html, flash",supplied:"mp3",preload:"metadata",volume:0.8,muted:false,backgroundColor:"#000000",cssSelectorAncestor:"#jp_interface_1",cssSelector:{videoPlay:".jp-video-play",play:".jp-play",pause:".jp-pause",stop:".jp-stop",seekBar:".jp-seek-bar",playBar:".jp-play-bar",mute:".jp-mute",unmute:".jp-unmute", 21 | volumeBar:".jp-volume-bar",volumeBarValue:".jp-volume-bar-value",currentTime:".jp-current-time",duration:".jp-duration"},idPrefix:"jp",errorAlerts:false,warningAlerts:false},instances:{},status:{src:"",media:{},paused:true,format:{},formatType:"",waitForPlay:true,waitForLoad:true,srcSet:false,video:false,seekPercent:0,currentPercentRelative:0,currentPercentAbsolute:0,currentTime:0,duration:0},_status:{volume:h,muted:false,width:0,height:0},internal:{ready:false,instance:h,htmlDlyCmdId:h},solution:{html:true, 22 | flash:true},format:{mp3:{codec:'audio/mpeg; codecs="mp3"',flashCanPlay:true,media:"audio"},m4a:{codec:'audio/mp4; codecs="mp4a.40.2"',flashCanPlay:true,media:"audio"},oga:{codec:'audio/ogg; codecs="vorbis"',flashCanPlay:false,media:"audio"},wav:{codec:'audio/wav; codecs="1"',flashCanPlay:false,media:"audio"},webma:{codec:'audio/webm; codecs="vorbis"',flashCanPlay:false,media:"audio"},m4v:{codec:'video/mp4; codecs="avc1.42E01E, mp4a.40.2"',flashCanPlay:true,media:"video"},ogv:{codec:'video/ogg; codecs="theora, vorbis"', 23 | flashCanPlay:false,media:"video"},webmv:{codec:'video/webm; codecs="vorbis, vp8"',flashCanPlay:false,media:"video"}},_init:function(){var a=this;this.element.empty();this.status=c.extend({},this.status,this._status);this.internal=c.extend({},this.internal);this.formats=[];this.solutions=[];this.require={};this.htmlElement={};this.html={};this.html.audio={};this.html.video={};this.flash={};this.css={};this.css.cs={};this.css.jq={};this.status.volume=this._limitValue(this.options.volume,0,1);this.status.muted= 24 | this.options.muted;this.status.width=this.element.css("width");this.status.height=this.element.css("height");this.element.css({"background-color":this.options.backgroundColor});c.each(this.options.supplied.toLowerCase().split(","),function(e,g){var i=g.replace(/^\s+|\s+$/g,"");if(a.format[i]){var j=false;c.each(a.formats,function(n,k){if(i===k){j=true;return false}});j||a.formats.push(i)}});c.each(this.options.solution.toLowerCase().split(","),function(e,g){var i=g.replace(/^\s+|\s+$/g,"");if(a.solution[i]){var j= 25 | false;c.each(a.solutions,function(n,k){if(i===k){j=true;return false}});j||a.solutions.push(i)}});this.internal.instance="jp_"+this.count;this.instances[this.internal.instance]=this.element;this.element.attr("id")===""&&this.element.attr("id",this.options.idPrefix+"_jplayer_"+this.count);this.internal.self=c.extend({},{id:this.element.attr("id"),jq:this.element});this.internal.audio=c.extend({},{id:this.options.idPrefix+"_audio_"+this.count,jq:h});this.internal.video=c.extend({},{id:this.options.idPrefix+ 26 | "_video_"+this.count,jq:h});this.internal.flash=c.extend({},{id:this.options.idPrefix+"_flash_"+this.count,jq:h,swf:this.options.swfPath+(this.options.swfPath!==""&&this.options.swfPath.slice(-1)!=="/"?"/":"")+"Jplayer.swf"});this.internal.poster=c.extend({},{id:this.options.idPrefix+"_poster_"+this.count,jq:h});c.each(c.jPlayer.event,function(e,g){if(a.options[e]!==h){a.element.bind(g+".jPlayer",a.options[e]);a.options[e]=h}});this.htmlElement.poster=document.createElement("img");this.htmlElement.poster.id= 27 | this.internal.poster.id;this.htmlElement.poster.onload=function(){if(!a.status.video||a.status.waitForPlay)a.internal.poster.jq.show()};this.element.append(this.htmlElement.poster);this.internal.poster.jq=c("#"+this.internal.poster.id);this.internal.poster.jq.css({width:this.status.width,height:this.status.height});this.internal.poster.jq.hide();this.require.audio=false;this.require.video=false;c.each(this.formats,function(e,g){a.require[a.format[g].media]=true});this.html.audio.available=false;if(this.require.audio){this.htmlElement.audio= 28 | document.createElement("audio");this.htmlElement.audio.id=this.internal.audio.id;this.html.audio.available=!!this.htmlElement.audio.canPlayType}this.html.video.available=false;if(this.require.video){this.htmlElement.video=document.createElement("video");this.htmlElement.video.id=this.internal.video.id;this.html.video.available=!!this.htmlElement.video.canPlayType}this.flash.available=this._checkForFlash(10);this.html.canPlay={};this.flash.canPlay={};c.each(this.formats,function(e,g){a.html.canPlay[g]= 29 | a.html[a.format[g].media].available&&""!==a.htmlElement[a.format[g].media].canPlayType(a.format[g].codec);a.flash.canPlay[g]=a.format[g].flashCanPlay&&a.flash.available});this.html.desired=false;this.flash.desired=false;c.each(this.solutions,function(e,g){if(e===0)a[g].desired=true;else{var i=false,j=false;c.each(a.formats,function(n,k){if(a[a.solutions[0]].canPlay[k])if(a.format[k].media==="video")j=true;else i=true});a[g].desired=a.require.audio&&!i||a.require.video&&!j}});this.html.support={}; 30 | this.flash.support={};c.each(this.formats,function(e,g){a.html.support[g]=a.html.canPlay[g]&&a.html.desired;a.flash.support[g]=a.flash.canPlay[g]&&a.flash.desired});this.html.used=false;this.flash.used=false;c.each(this.solutions,function(e,g){c.each(a.formats,function(i,j){if(a[g].support[j]){a[g].used=true;return false}})});this.html.used||this.flash.used||this._error({type:c.jPlayer.error.NO_SOLUTION,context:"{solution:'"+this.options.solution+"', supplied:'"+this.options.supplied+"'}",message:c.jPlayer.errorMsg.NO_SOLUTION, 31 | hint:c.jPlayer.errorHint.NO_SOLUTION});this.html.active=false;this.html.audio.gate=false;this.html.video.gate=false;this.flash.active=false;this.flash.gate=false;if(this.flash.used){var b="id="+escape(this.internal.self.id)+"&vol="+this.status.volume+"&muted="+this.status.muted;if(c.browser.msie&&Number(c.browser.version)<=8){var d='';f[1]='';f[2]='';f[3]='';f[4]='';b=document.createElement(d);for(d=0;d0?100*d/this.status.duration:0;if(typeof a.seekable==="object"&&a.seekable.length>0){e=this.status.duration>0?100*a.seekable.end(a.seekable.length-1)/this.status.duration:100;g=100*a.currentTime/a.seekable.end(a.seekable.length-1)}else{e=100;g=f}if(b)f=g=d=0;this.status.seekPercent=e;this.status.currentPercentRelative=g;this.status.currentPercentAbsolute=f;this.status.currentTime=d},_resetStatus:function(){this.status=c.extend({},this.status,c.jPlayer.prototype.status)}, 43 | _trigger:function(a,b,d){a=c.Event(a);a.jPlayer={};a.jPlayer.version=c.extend({},this.version);a.jPlayer.status=c.extend(true,{},this.status);a.jPlayer.html=c.extend(true,{},this.html);a.jPlayer.flash=c.extend(true,{},this.flash);if(b)a.jPlayer.error=c.extend({},b);if(d)a.jPlayer.warning=c.extend({},d);this.element.trigger(a)},jPlayerFlashEvent:function(a,b){if(a===c.jPlayer.event.ready&&!this.internal.ready){this.internal.ready=true;this.version.flash=b.version;this.version.needFlash!==this.version.flash&& 44 | this._error({type:c.jPlayer.error.VERSION,context:this.version.flash,message:c.jPlayer.errorMsg.VERSION+this.version.flash,hint:c.jPlayer.errorHint.VERSION});this._trigger(a)}if(this.flash.gate)switch(a){case c.jPlayer.event.progress:this._getFlashStatus(b);this._updateInterface();this._trigger(a);break;case c.jPlayer.event.timeupdate:this._getFlashStatus(b);this._updateInterface();this._trigger(a);break;case c.jPlayer.event.play:this._seeked();this._updateButtons(true);this._trigger(a);break;case c.jPlayer.event.pause:this._updateButtons(false); 45 | this._trigger(a);break;case c.jPlayer.event.ended:this._updateButtons(false);this._trigger(a);break;case c.jPlayer.event.error:this.status.waitForLoad=true;this.status.waitForPlay=true;this.status.video&&this.internal.flash.jq.css({width:"0px",height:"0px"});this._validString(this.status.media.poster)&&this.internal.poster.jq.show();this.css.jq.videoPlay.length&&this.css.jq.videoPlay.show();this.status.video?this._flash_setVideo(this.status.media):this._flash_setAudio(this.status.media);this._error({type:c.jPlayer.error.URL, 46 | context:b.src,message:c.jPlayer.errorMsg.URL,hint:c.jPlayer.errorHint.URL});break;case c.jPlayer.event.seeking:this._seeking();this._trigger(a);break;case c.jPlayer.event.seeked:this._seeked();this._trigger(a);break;default:this._trigger(a)}return false},_getFlashStatus:function(a){this.status.seekPercent=a.seekPercent;this.status.currentPercentRelative=a.currentPercentRelative;this.status.currentPercentAbsolute=a.currentPercentAbsolute;this.status.currentTime=a.currentTime;this.status.duration=a.duration}, 47 | _updateButtons:function(a){this.status.paused=!a;if(this.css.jq.play.length&&this.css.jq.pause.length)if(a){this.css.jq.play.hide();this.css.jq.pause.show()}else{this.css.jq.play.show();this.css.jq.pause.hide()}},_updateInterface:function(){this.css.jq.seekBar.length&&this.css.jq.seekBar.width(this.status.seekPercent+"%");this.css.jq.playBar.length&&this.css.jq.playBar.width(this.status.currentPercentRelative+"%");this.css.jq.currentTime.length&&this.css.jq.currentTime.text(c.jPlayer.convertTime(this.status.currentTime)); 48 | this.css.jq.duration.length&&this.css.jq.duration.text(c.jPlayer.convertTime(this.status.duration))},_seeking:function(){this.css.jq.seekBar.length&&this.css.jq.seekBar.addClass("jp-seeking-bg")},_seeked:function(){this.css.jq.seekBar.length&&this.css.jq.seekBar.removeClass("jp-seeking-bg")},setMedia:function(a){var b=this;this._seeked();clearTimeout(this.internal.htmlDlyCmdId);var d=this.html.audio.gate,f=this.html.video.gate,e=false;c.each(this.formats,function(g,i){var j=b.format[i].media==="video"; 49 | c.each(b.solutions,function(n,k){if(b[k].support[i]&&b._validString(a[i])){var l=k==="html";if(j)if(l){b.html.audio.gate=false;b.html.video.gate=true;b.flash.gate=false}else{b.html.audio.gate=false;b.html.video.gate=false;b.flash.gate=true}else if(l){b.html.audio.gate=true;b.html.video.gate=false;b.flash.gate=false}else{b.html.audio.gate=false;b.html.video.gate=false;b.flash.gate=true}if(b.flash.active||b.html.active&&b.flash.gate||d===b.html.audio.gate&&f===b.html.video.gate)b.clearMedia();else if(d!== 50 | b.html.audio.gate&&f!==b.html.video.gate){b._html_pause();b.status.video&&b.internal.video.jq.css({width:"0px",height:"0px"});b._resetStatus()}if(j){if(l){b._html_setVideo(a);b.html.active=true;b.flash.active=false}else{b._flash_setVideo(a);b.html.active=false;b.flash.active=true}b.css.jq.videoPlay.length&&b.css.jq.videoPlay.show();b.status.video=true}else{if(l){b._html_setAudio(a);b.html.active=true;b.flash.active=false}else{b._flash_setAudio(a);b.html.active=false;b.flash.active=true}b.css.jq.videoPlay.length&& 51 | b.css.jq.videoPlay.hide();b.status.video=false}e=true;return false}});if(e)return false});if(e){if(this._validString(a.poster))if(this.htmlElement.poster.src!==a.poster)this.htmlElement.poster.src=a.poster;else this.internal.poster.jq.show();else this.internal.poster.jq.hide();this.status.srcSet=true;this.status.media=c.extend({},a);this._updateButtons(false);this._updateInterface()}else{this.status.srcSet&&!this.status.waitForPlay&&this.pause();this.html.audio.gate=false;this.html.video.gate=false; 52 | this.flash.gate=false;this.html.active=false;this.flash.active=false;this._resetStatus();this._updateInterface();this._updateButtons(false);this.internal.poster.jq.hide();this.html.used&&this.require.video&&this.internal.video.jq.css({width:"0px",height:"0px"});this.flash.used&&this.internal.flash.jq.css({width:"0px",height:"0px"});this._error({type:c.jPlayer.error.NO_SUPPORT,context:"{supplied:'"+this.options.supplied+"'}",message:c.jPlayer.errorMsg.NO_SUPPORT,hint:c.jPlayer.errorHint.NO_SUPPORT})}}, 53 | clearMedia:function(){this._resetStatus();this._updateButtons(false);this.internal.poster.jq.hide();clearTimeout(this.internal.htmlDlyCmdId);if(this.html.active)this._html_clearMedia();else this.flash.active&&this._flash_clearMedia()},load:function(){if(this.status.srcSet)if(this.html.active)this._html_load();else this.flash.active&&this._flash_load();else this._urlNotSetError("load")},play:function(a){a=typeof a==="number"?a:NaN;if(this.status.srcSet)if(this.html.active)this._html_play(a);else this.flash.active&& 54 | this._flash_play(a);else this._urlNotSetError("play")},videoPlay:function(){this.play()},pause:function(a){a=typeof a==="number"?a:NaN;if(this.status.srcSet)if(this.html.active)this._html_pause(a);else this.flash.active&&this._flash_pause(a);else this._urlNotSetError("pause")},pauseOthers:function(){var a=this;c.each(this.instances,function(b,d){a.element!==d&&d.data("jPlayer").status.srcSet&&d.jPlayer("pause")})},stop:function(){if(this.status.srcSet)if(this.html.active)this._html_pause(0);else this.flash.active&& 55 | this._flash_pause(0);else this._urlNotSetError("stop")},playHead:function(a){a=this._limitValue(a,0,100);if(this.status.srcSet)if(this.html.active)this._html_playHead(a);else this.flash.active&&this._flash_playHead(a);else this._urlNotSetError("playHead")},mute:function(){this.status.muted=true;this.html.used&&this._html_mute(true);this.flash.used&&this._flash_mute(true);this._updateMute(true);this._updateVolume(0);this._trigger(c.jPlayer.event.volumechange)},unmute:function(){this.status.muted=false; 56 | this.html.used&&this._html_mute(false);this.flash.used&&this._flash_mute(false);this._updateMute(false);this._updateVolume(this.status.volume);this._trigger(c.jPlayer.event.volumechange)},_updateMute:function(a){if(this.css.jq.mute.length&&this.css.jq.unmute.length)if(a){this.css.jq.mute.hide();this.css.jq.unmute.show()}else{this.css.jq.mute.show();this.css.jq.unmute.hide()}},volume:function(a){a=this._limitValue(a,0,1);this.status.volume=a;this.html.used&&this._html_volume(a);this.flash.used&&this._flash_volume(a); 57 | this.status.muted||this._updateVolume(a);this._trigger(c.jPlayer.event.volumechange)},volumeBar:function(a){if(!this.status.muted&&this.css.jq.volumeBar){var b=this.css.jq.volumeBar.offset();a=a.pageX-b.left;b=this.css.jq.volumeBar.width();this.volume(a/b)}},volumeBarValue:function(a){this.volumeBar(a)},_updateVolume:function(a){this.css.jq.volumeBarValue.length&&this.css.jq.volumeBarValue.width(a*100+"%")},_volumeFix:function(a){var b=0.0010*Math.random();return a+(a<0.5?b:-b)},_cssSelectorAncestor:function(a, 58 | b){this.options.cssSelectorAncestor=a;b&&c.each(this.options.cssSelector,function(d,f){self._cssSelector(d,f)})},_cssSelector:function(a,b){var d=this;if(typeof b==="string")if(c.jPlayer.prototype.options.cssSelector[a]){this.css.jq[a]&&this.css.jq[a].length&&this.css.jq[a].unbind(".jPlayer");this.options.cssSelector[a]=b;this.css.cs[a]=this.options.cssSelectorAncestor+" "+b;this.css.jq[a]=b?c(this.css.cs[a]):[];this.css.jq[a].length&&this.css.jq[a].bind("click.jPlayer",function(f){d[a](f);c(this).blur(); 59 | return false});b&&this.css.jq[a].length!==1&&this._warning({type:c.jPlayer.warning.CSS_SELECTOR_COUNT,context:this.css.cs[a],message:c.jPlayer.warningMsg.CSS_SELECTOR_COUNT+this.css.jq[a].length+" found for "+a+" method.",hint:c.jPlayer.warningHint.CSS_SELECTOR_COUNT})}else this._warning({type:c.jPlayer.warning.CSS_SELECTOR_METHOD,context:a,message:c.jPlayer.warningMsg.CSS_SELECTOR_METHOD,hint:c.jPlayer.warningHint.CSS_SELECTOR_METHOD});else this._warning({type:c.jPlayer.warning.CSS_SELECTOR_STRING, 60 | context:b,message:c.jPlayer.warningMsg.CSS_SELECTOR_STRING,hint:c.jPlayer.warningHint.CSS_SELECTOR_STRING})},seekBar:function(a){if(this.css.jq.seekBar){var b=this.css.jq.seekBar.offset();a=a.pageX-b.left;b=this.css.jq.seekBar.width();this.playHead(100*a/b)}},playBar:function(a){this.seekBar(a)},currentTime:function(){},duration:function(){},option:function(a,b){var d=a;if(arguments.length===0)return c.extend(true,{},this.options);if(typeof a==="string"){var f=a.split(".");if(b===h){for(var e=c.extend(true, 61 | {},this.options),g=0;g=9||this.htmlElement.media.load()}},_html_load:function(){if(this.status.waitForLoad){this.status.waitForLoad=false;this.htmlElement.media.src=this.status.src; 65 | try{this.htmlElement.media.load()}catch(a){}}clearTimeout(this.internal.htmlDlyCmdId)},_html_play:function(a){var b=this;this._html_load();this.htmlElement.media.play();if(!isNaN(a))try{this.htmlElement.media.currentTime=a}catch(d){this.internal.htmlDlyCmdId=setTimeout(function(){b.play(a)},100);return}this._html_checkWaitForPlay()},_html_pause:function(a){var b=this;a>0?this._html_load():clearTimeout(this.internal.htmlDlyCmdId);this.htmlElement.media.pause();if(!isNaN(a))try{this.htmlElement.media.currentTime= 66 | a}catch(d){this.internal.htmlDlyCmdId=setTimeout(function(){b.pause(a)},100);return}a>0&&this._html_checkWaitForPlay()},_html_playHead:function(a){var b=this;this._html_load();try{if(typeof this.htmlElement.media.seekable==="object"&&this.htmlElement.media.seekable.length>0)this.htmlElement.media.currentTime=a*this.htmlElement.media.seekable.end(this.htmlElement.media.seekable.length-1)/100;else if(this.htmlElement.media.duration>0&&!isNaN(this.htmlElement.media.duration))this.htmlElement.media.currentTime= 67 | a*this.htmlElement.media.duration/100;else throw"e";}catch(d){this.internal.htmlDlyCmdId=setTimeout(function(){b.playHead(a)},100);return}this.status.waitForLoad||this._html_checkWaitForPlay()},_html_checkWaitForPlay:function(){if(this.status.waitForPlay){this.status.waitForPlay=false;this.css.jq.videoPlay.length&&this.css.jq.videoPlay.hide();if(this.status.video){this.internal.poster.jq.hide();this.internal.video.jq.css({width:this.status.width,height:this.status.height})}}},_html_volume:function(a){if(this.html.audio.available)this.htmlElement.audio.volume= 68 | a;if(this.html.video.available)this.htmlElement.video.volume=a},_html_mute:function(a){if(this.html.audio.available)this.htmlElement.audio.muted=a;if(this.html.video.available)this.htmlElement.video.muted=a},_flash_setAudio:function(a){var b=this;try{c.each(this.formats,function(f,e){if(b.flash.support[e]&&a[e]){switch(e){case "m4a":b._getMovie().fl_setAudio_m4a(a[e]);break;case "mp3":b._getMovie().fl_setAudio_mp3(a[e])}b.status.src=a[e];b.status.format[e]=true;b.status.formatType=e;return false}}); 69 | if(this.options.preload==="auto"){this._flash_load();this.status.waitForLoad=false}}catch(d){this._flashError(d)}},_flash_setVideo:function(a){var b=this;try{c.each(this.formats,function(f,e){if(b.flash.support[e]&&a[e]){switch(e){case "m4v":b._getMovie().fl_setVideo_m4v(a[e])}b.status.src=a[e];b.status.format[e]=true;b.status.formatType=e;return false}});if(this.options.preload==="auto"){this._flash_load();this.status.waitForLoad=false}}catch(d){this._flashError(d)}},_flash_clearMedia:function(){this.internal.flash.jq.css({width:"0px", 70 | height:"0px"});try{this._getMovie().fl_clearMedia()}catch(a){this._flashError(a)}},_flash_load:function(){try{this._getMovie().fl_load()}catch(a){this._flashError(a)}this.status.waitForLoad=false},_flash_play:function(a){try{this._getMovie().fl_play(a)}catch(b){this._flashError(b)}this.status.waitForLoad=false;this._flash_checkWaitForPlay()},_flash_pause:function(a){try{this._getMovie().fl_pause(a)}catch(b){this._flashError(b)}if(a>0){this.status.waitForLoad=false;this._flash_checkWaitForPlay()}}, 71 | _flash_playHead:function(a){try{this._getMovie().fl_play_head(a)}catch(b){this._flashError(b)}this.status.waitForLoad||this._flash_checkWaitForPlay()},_flash_checkWaitForPlay:function(){if(this.status.waitForPlay){this.status.waitForPlay=false;this.css.jq.videoPlay.length&&this.css.jq.videoPlay.hide();if(this.status.video){this.internal.poster.jq.hide();this.internal.flash.jq.css({width:this.status.width,height:this.status.height})}}},_flash_volume:function(a){try{this._getMovie().fl_volume(a)}catch(b){this._flashError(b)}}, 72 | _flash_mute:function(a){try{this._getMovie().fl_mute(a)}catch(b){this._flashError(b)}},_getMovie:function(){return document[this.internal.flash.id]},_checkForFlash:function(a){var b=false,d;if(window.ActiveXObject)try{new ActiveXObject("ShockwaveFlash.ShockwaveFlash."+a);b=true}catch(f){}else if(navigator.plugins&&navigator.mimeTypes.length>0)if(d=navigator.plugins["Shockwave Flash"])if(navigator.plugins["Shockwave Flash"].description.replace(/.*\s(\d+\.\d+).*/,"$1")>=a)b=true;return c.browser.msie&& 73 | Number(c.browser.version)>=9?false:b},_validString:function(a){return a&&typeof a==="string"},_limitValue:function(a,b,d){return ad?d:a},_urlNotSetError:function(a){this._error({type:c.jPlayer.error.URL_NOT_SET,context:a,message:c.jPlayer.errorMsg.URL_NOT_SET,hint:c.jPlayer.errorHint.URL_NOT_SET})},_flashError:function(a){this._error({type:c.jPlayer.error.FLASH,context:this.internal.flash.swf,message:c.jPlayer.errorMsg.FLASH+a.message,hint:c.jPlayer.errorHint.FLASH})},_error:function(a){this._trigger(c.jPlayer.event.error, 74 | a);if(this.options.errorAlerts)this._alert("Error!"+(a.message?"\n\n"+a.message:"")+(a.hint?"\n\n"+a.hint:"")+"\n\nContext: "+a.context)},_warning:function(a){this._trigger(c.jPlayer.event.warning,h,a);if(this.options.errorAlerts)this._alert("Warning!"+(a.message?"\n\n"+a.message:"")+(a.hint?"\n\n"+a.hint:"")+"\n\nContext: "+a.context)},_alert:function(a){alert("jPlayer "+this.version.script+" : id='"+this.internal.self.id+"' : "+a)}};c.jPlayer.error={FLASH:"e_flash",NO_SOLUTION:"e_no_solution",NO_SUPPORT:"e_no_support", 75 | URL:"e_url",URL_NOT_SET:"e_url_not_set",VERSION:"e_version"};c.jPlayer.errorMsg={FLASH:"jPlayer's Flash fallback is not configured correctly, or a command was issued before the jPlayer Ready event. Details: ",NO_SOLUTION:"No solution can be found by jPlayer in this browser. Neither HTML nor Flash can be used.",NO_SUPPORT:"It is not possible to play any media format provided in setMedia() on this browser using your current options.",URL:"Media URL could not be loaded.",URL_NOT_SET:"Attempt to issue media playback commands, while no media url is set.", 76 | VERSION:"jPlayer "+c.jPlayer.prototype.version.script+" needs Jplayer.swf version "+c.jPlayer.prototype.version.needFlash+" but found "};c.jPlayer.errorHint={FLASH:"Check your swfPath option and that Jplayer.swf is there.",NO_SOLUTION:"Review the jPlayer options: support and supplied.",NO_SUPPORT:"Video or audio formats defined in the supplied option are missing.",URL:"Check media URL is valid.",URL_NOT_SET:"Use setMedia() to set the media URL.",VERSION:"Update jPlayer files."};c.jPlayer.warning= 77 | {CSS_SELECTOR_COUNT:"e_css_selector_count",CSS_SELECTOR_METHOD:"e_css_selector_method",CSS_SELECTOR_STRING:"e_css_selector_string",OPTION_KEY:"e_option_key"};c.jPlayer.warningMsg={CSS_SELECTOR_COUNT:"The number of methodCssSelectors found did not equal one: ",CSS_SELECTOR_METHOD:"The methodName given in jPlayer('cssSelector') is not a valid jPlayer method.",CSS_SELECTOR_STRING:"The methodCssSelector given in jPlayer('cssSelector') is not a String or is empty.",OPTION_KEY:"The option requested in jPlayer('option') is undefined."}; 78 | c.jPlayer.warningHint={CSS_SELECTOR_COUNT:"Check your css selector and the ancestor.",CSS_SELECTOR_METHOD:"Check your method name.",CSS_SELECTOR_STRING:"Check your css selector is a string.",OPTION_KEY:"Check your option name."}})(jQuery); -------------------------------------------------------------------------------- /scripts/jquery.rotate.js: -------------------------------------------------------------------------------- 1 | /* 2 | * rotate: A jQuery cssHooks adding a cross browser 'rotate' property to $.fn.css() and $.fn.animate() 3 | * 4 | * Limitations: 5 | * - requires jQuery 1.4.3+ 6 | * - cannot be used together with jquery.scale.js 7 | * 8 | * Copyright (c) 2010 Louis-Rémi Babé twitter.com/louis_remi 9 | * Licensed under the MIT license. 10 | * 11 | * This saved you an hour of work? 12 | * Send me music http://www.amazon.fr/wishlist/HNTU0468LQON 13 | * 14 | */ 15 | (function($) { 16 | 17 | var div = document.createElement('div'), 18 | divStyle = div.style, 19 | support = $.support; 20 | 21 | support.transform = 22 | divStyle.MozTransform === ''? 'MozTransform' : 23 | (divStyle.MsTransform === ''? 'MsTransform' : 24 | (divStyle.WebkitTransform === ''? 'WebkitTransform' : 25 | (divStyle.OTransform === ''? 'OTransform' : 26 | (divStyle.transform === ''? 'transform' : 27 | false)))); 28 | support.matrixFilter = !support.transform && divStyle.filter === ''; 29 | div = null; 30 | 31 | $.cssNumber.rotate = true; 32 | $.cssHooks.rotate = { 33 | set: function( elem, value ) { 34 | var _support = support, 35 | supportTransform = _support.transform, 36 | cos, sin, 37 | centerOrigin; 38 | 39 | if (typeof value === 'string') { 40 | value = toRadian(value); 41 | } 42 | 43 | $.data( elem, 'transform', { 44 | rotate: value 45 | }); 46 | 47 | if (supportTransform) { 48 | elem.style[supportTransform] = 'rotate('+ value +'rad)'; 49 | 50 | } else if (_support.matrixFilter) { 51 | cos = Math.cos(value); 52 | sin = Math.sin(value); 53 | elem.style.filter = [ 54 | "progid:DXImageTransform.Microsoft.Matrix(", 55 | "M11="+cos+",", 56 | "M12="+(-sin)+",", 57 | "M21="+sin+",", 58 | "M22="+cos+",", 59 | "SizingMethod='auto expand'", 60 | ")" 61 | ].join(''); 62 | 63 | // From pbakaus's Transformie http://github.com/pbakaus/transformie 64 | if(centerOrigin = $.rotate.centerOrigin) { 65 | elem.style[centerOrigin == 'margin' ? 'marginLeft' : 'left'] = -(elem.offsetWidth/2) + (elem.clientWidth/2) + "px"; 66 | elem.style[centerOrigin == 'margin' ? 'marginTop' : 'top'] = -(elem.offsetHeight/2) + (elem.clientHeight/2) + "px"; 67 | } 68 | } 69 | }, 70 | get: function( elem, computed ) { 71 | var transform = $.data( elem, 'transform' ); 72 | return transform && transform.rotate? transform.rotate : 0; 73 | } 74 | }; 75 | $.fx.step.rotate = function( fx ) { 76 | $.cssHooks.rotate.set( fx.elem, fx.now+fx.unit ); 77 | }; 78 | 79 | function radToDeg( rad ) { 80 | return rad * 180 / Math.PI; 81 | } 82 | function toRadian(value) { 83 | if( ~value.indexOf("deg") ) { 84 | return parseInt(value,10) * (Math.PI * 2 / 360); 85 | } else if ( ~value.indexOf("grad") ) { 86 | return parseInt(value,10) * (Math.PI/200); 87 | } 88 | return parseFloat(value); 89 | } 90 | 91 | $.rotate = { 92 | centerOrigin: 'margin', 93 | radToDeg: radToDeg 94 | }; 95 | 96 | })(jQuery); -------------------------------------------------------------------------------- /scripts/main.js: -------------------------------------------------------------------------------- 1 | var targetOrigin = '*'; 2 | 3 | window.onload = function() { 4 | initFolders(); 5 | 6 | //createAnchors(); 7 | //fixZIndex() 8 | //createCloud(); 9 | //addDelayToTags(); 10 | 11 | if (document.location.hash == "#xxxie") { 12 | document.body.classList.add("activexxxie"); 13 | } 14 | 15 | var demos = document.querySelectorAll("#wall > section > article > article .demo"); 16 | for (var i = 0; i < demos.length; i++) { 17 | var d = demos[i]; 18 | d.style.marginLeft = (-d.clientWidth / 2) + "px"; 19 | } 20 | 21 | window.addEventListener("message", function(e) { 22 | if ("stop_demo" == e.data) { 23 | window.parent.postMessage('finished_exit', targetOrigin); 24 | } 25 | }, true); 26 | 27 | document.body.classList.remove("loading"); 28 | window.parent.postMessage('loaded', targetOrigin); 29 | } 30 | 31 | function addDelayToTags() { 32 | var s = document.querySelectorAll("#overview span"); 33 | for (var i = 0; i < s.length; i++) { 34 | s[i].style.MozTransitionDelay = (~~(Math.random() * 50))/10 + "s"; 35 | } 36 | } 37 | 38 | function fixZIndex() { 39 | var a = document.querySelectorAll("#wall > section > article > article"); 40 | for (var i = 0; i < a.length; i++) { 41 | var article = a[i]; 42 | (function(article) { 43 | article.addEventListener("mouseover", function() { 44 | article.style.zIndex = 4000; 45 | }, true); 46 | article.addEventListener("mouseout", function(e) { 47 | article.style.zIndex = 3999; 48 | }, true); 49 | })(article); 50 | } 51 | } 52 | 53 | function createAnchors() { 54 | var h1 = document.querySelectorAll('#wall > section > article > h1'); 55 | for (var i = 0; i < h1.length; i++) { h1[i].id = h1[i].innerHTML; } 56 | h1 = document.querySelectorAll('#wall > section > article > article > h1'); 57 | for (var i = 0; i < h1.length; i++) { h1[i].id = h1[i].innerHTML; } 58 | } 59 | 60 | function initFolders() { 61 | var h1 = document.querySelectorAll("#wall > section > article > h1"); 62 | 63 | for (var i = 0; i < h1.length; i++) { 64 | var elt = h1[i]; 65 | 66 | (function(elt) { 67 | elt.addEventListener("click", function() { 68 | var p = elt.parentNode; 69 | 70 | if (p.classList.contains("closed")) { 71 | p.classList.remove("closed"); 72 | } else { 73 | p.classList.add("closed"); 74 | } 75 | }, true); 76 | })(elt); 77 | } 78 | } 79 | 80 | function findPos(e) { 81 | var curleft = curtop = 0; 82 | if (e.offsetParent) { 83 | do { 84 | curleft += e.offsetLeft; 85 | curtop += e.offsetTop; 86 | } while (e = e.offsetParent); 87 | } 88 | return {left: curleft, top: curtop}; 89 | } 90 | 91 | 92 | // some specific demos scripts 93 | 94 | // text-shadow demo 95 | window.addEventListener("load", function() { 96 | var div = document.querySelector("#textshadowdemo > div"); 97 | var p = document.querySelector("#textshadowdemo > div > p"); 98 | var pos = findPos(div); 99 | pos.left += Math.round(div.clientWidth / 2) - 72; 100 | pos.top += Math.round(div.clientHeight / 2) + 500; 101 | div.parentNode.addEventListener("mousemove", function(e) { 102 | var x = (e.clientX - (pos.left - window.scrollX)); 103 | var y = (e.clientY - (pos.top - window.scrollY)); 104 | x *= -0.1; 105 | y *= -0.1; 106 | p.style.textShadow = x + "px " + y + "px 4px black"; 107 | 108 | }, true); 109 | }, true); 110 | 111 | 112 | // Form demo 113 | function submitForm() { 114 | try { 115 | var inputs = document.querySelectorAll("#formdemo input"); 116 | var who = inputs[0].value; 117 | var demois = inputs[1].value; 118 | var msg = inputs[2].value; 119 | var xhr = new XMLHttpRequest(); 120 | xhr.open("GET", "http://184.106.112.6:9384/yo?WHO=" + who + "&DEMOIS=" + demois + "&MSG=" + msg); 121 | xhr.send(); 122 | } catch(e) {} 123 | document.querySelector("#formdemo").className = "submited"; 124 | return false; 125 | } 126 | 127 | // Open / Close all sections 128 | function toggleFold() { 129 | var a = document.querySelectorAll(".closed"); 130 | if (a.length == 0) { 131 | var a = document.querySelectorAll("#wall > section > article"); 132 | for (var i = 0; i < a.length; i++) { 133 | a[i].classList.add("closed"); 134 | } 135 | } else { 136 | for (var i = 0; i < a.length; i++) { 137 | a[i].classList.remove("closed"); 138 | } 139 | } 140 | } 141 | 142 | function createCloud() { 143 | var Bh1 = document.querySelectorAll('#wall > section > article > h1'); 144 | var Mh1 = document.querySelectorAll('#wall > section > article > article > h1'); 145 | 146 | var txt = ""; 147 | 148 | for (var i = 0; i < Bh1.length; i++) { 149 | txt += 'u\'' + Bh1[i].innerHTML + '\' : 100,\n'; 150 | } 151 | 152 | for (var i = 0; i < Mh1.length; i++) { 153 | txt += 'u\'' + Mh1[i].innerHTML + '\' : ' + (20 + Mh1.length - i)+ ',\n'; 154 | } 155 | 156 | alert(txt); 157 | 158 | } 159 | -------------------------------------------------------------------------------- /scripts/orientation.js: -------------------------------------------------------------------------------- 1 | var orientationdemo; 2 | 3 | window.addEventListener("load", function() { 4 | orientationdemo = document.querySelector("#orientationdemo > .demo"); 5 | }, true); 6 | 7 | window.addEventListener("MozOrientation", function(e) { 8 | if (!orientationdemo) return; 9 | var angle = (e.x * 90) + "deg"; 10 | orientationdemo.style.MozTransform = "rotate(" + angle + ")"; 11 | }, true); 12 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | /***** @font-face *****/ 2 | @font-face { font-family: bebas; src: local("BebasNeue") url(fonts/BebasNeue.otf);} 3 | @font-face { font-family: ubuntu; src: local("Ubuntu") url(fonts/ubuntu.ttf);} 4 | @font-face { font-family: marketing; src: local("MarketingScript") url(fonts/MarketingScript.ttf);} 5 | @font-face { font-family: megalopolis; src: local("MEgalopolisExtra") url(fonts/MEgalopolisExtra.woff);} 6 | 7 | 8 | /***** general stuff *****/ 9 | 10 | body { font-family: ubuntu; } 11 | 12 | #plzwait { display: none; } 13 | body.loading #plzwait {display: block; width: 100%; height: 100%; text-align: center; padding-top: 200px;} 14 | body.loading #wall {opacity: 0;} 15 | 16 | h1, h2, h3, h4 { font-family: bebas; } 17 | * {padding: 0px; margin: 0px;} 18 | body { background-color: #343838; } 19 | html { box-shadow: inset 0px 0px 20px black; } 20 | 21 | #wall { background-color: #E1E6E9; width: 900px; padding: 20px; margin: auto; margin-top: 10px; box-shadow: 0px 0px 5px black;} 22 | 23 | #wall > h1 {font-size: 90px; width: 500px; vertical-align: middle; font-weight: normal; text-align: center; margin: auto; margin-top: 30px; text-align: center; position: relative;} 24 | #wall > h1 > span {font-size: 30px; position: absolute; bottom: -2px; right: 48px; color: #999;} 25 | 26 | #shortcredit a { color: #555; } 27 | #shortcredit { opacity: 0.5; top: -150px; right: 0px; font-size: 10px; color: #777; position: relative; width: 100%; text-align: right; } 28 | #shortcredit:hover {opacity: 1;} 29 | 30 | #longcredits a { color: black; } 31 | #longcredits { 32 | color: #555; 33 | } 34 | 35 | /***** tag board *****/ 36 | 37 | #overview { margin: 5px; position: relative; background-color: #343838; box-shadow: inset 0px 0px 20px black; height: 320px; border-radius: 10px; margin-left: -5px; margin-right: -5px; } 38 | 39 | .tag {font-size: 18px; transition-duration: 1s; -webkit-transition-duration: 1s; -moz-transition-duration: 1s; cursor: default;} 40 | .tag.big {font-size: 50px;} 41 | .tag { margin-top: 10px; margin-left: 15px; font-family: bebas; position: absolute; text-decoration: none; } 42 | .tag.c0{color: #005f6b;} 43 | .tag.c0:hover {color: #00a2b7;} 44 | .tag.c1{color: #008c9e;} 45 | .tag.c1:hover {color: #00cfea;} 46 | .tag.c2{color: #00dffc;} 47 | .tag.c2:hover {color: #00e1ff;} 48 | .tag.c3{color: #00b4cc;} 49 | .tag.c3:hover {color: #00e1ff;} 50 | 51 | /**********************/ 52 | 53 | /* Title */ 54 | #wall > section > h1 { font-size: 100px; line-height: 100px; color: #2EC4DD; font-weight: normal; border-top: 2px solid black; padding-top: 20px; } 55 | #wall > section:nth-of-type(2) > h1 { margin-top: 40px; border-top: none; } 56 | #wall > section > article > h1:after { content: "▾"; margin-left: 25px; font-size: 50px; vertical-align: middle; -moz-transition-duration: 1s; -moz-transform: rotate(0deg); -webkit-transition-duration: 1s; -webkit-transform: rotate(0deg); transition-duration: 1s; transform: rotate(0deg); } 57 | #wall > section > article > h1 { font-size: 80px; height: 80px; font-weight: normal; background-image: url(imgs/dashed.png); background-repeat: repeat-x; background-position: left bottom; cursor: pointer; padding-bottom: 10px; -moz-transition: text-indent 0.1s; -webkit-transition: text-indent 0.1s; transition: text-indent 0.1s; } 58 | #wall > section > article > h1:hover { text-indent: 5px; } 59 | #wall > section > article > article > h1 { font-size: 32px; line-height: 32px; letter-spacing: 0px; vertical-align: bottom; font-weight: normal; position: absolute; bottom: 200px; } 60 | 61 | 62 | #wall > section > article { border-top: 10px solid #28C2DA; } 63 | #wall > section > article { line-height: 0px; } 64 | #wall > section > article * { line-height: normal; } 65 | #wall > section > article > article {transition-duration: 1s; -webkit-transition-duration: 1s; -moz-transition-duration: 1s; font-size: 12px; border-top: 0px solid #777; border-bottom: 1px solid #777; width: 225px; display: inline-block; margin-top: 10px; margin-bottom: 10px; padding-top: 10px; padding-bottom: 10px; margin-left: 6px; margin-right: 6px; height: 250px; vertical-align: middle; position: relative; padding-left: 30px; padding-right: 30px; } 66 | 67 | #wall > section > article.closed > h1:after { -moz-transform: rotate(-90deg); -webkit-transform: rotate(-90deg); transform: rotate(-90deg); } 68 | 69 | #wall > section > article.closed > article { height: 0px; border: none; margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px; } 70 | 71 | #wall > section > article > article { overflow: hidden;} 72 | 73 | #wall > section > article > article > p { line-height: 16px; margin-top: 65px; text-align: justify; position: relative; z-index: 41;} 74 | 75 | .demo { 76 | position: absolute; 77 | top: 400px; 78 | left: 50%; 79 | margin-left: -120px; 80 | background-color: white; 81 | border: 5px solid #28C2DA; 82 | z-index: 42; 83 | box-shadow: 0 0 30px black; 84 | -moz-transition-duration: 1s; 85 | -moz-transition-timing-function: cubic-bezier(0.05, 1.4, 0.25, 1); 86 | 87 | -webkit-transition-duration: 1s; 88 | -webkit-transition-timing-function: cubic-bezier(0.05, 1.4, 0.25, 1); 89 | 90 | transition-duration: 1s; 91 | transition-timing-function: cubic-bezier(0.05, 1.4, 0.25, 1); 92 | } 93 | 94 | article:hover > .demo { 95 | top: 100px; 96 | } 97 | 98 | article:hover > .demo.close { 99 | top: 400px; 100 | } 101 | 102 | .activexxxie .xxxie { 103 | opacity: 0.4; 104 | -moz-transform: scale(0.7); 105 | -webkit-transform: scale(0.7); 106 | transform: scale(0.7); 107 | } 108 | 109 | .activexxxie #overview .xxxie { 110 | opacity: 1; 111 | -moz-transform: scale(0)!important; 112 | -webkit-transform: scale(0)!important; 113 | transform: scale(0)!important; 114 | } 115 | 116 | #wall > section > article > article > a { 117 | position: absolute; 118 | z-index: 44; 119 | bottom: -30px; left: 0; 120 | -moz-transition-duration: 0.3s; 121 | -webkit-transition-duration: 0.3s; 122 | transition-duration: 0.3s; 123 | background-color: #28C2DA; 124 | line-height: 20px; 125 | text-align: center; 126 | color: white; 127 | width: 100%; 128 | font-weight: bold; 129 | box-shadow: 0px 0px 20px black; 130 | } 131 | 132 | #wall > section > article > article:hover > a { bottom: 0px; } 133 | 134 | article > .demo > * { 135 | display: block; 136 | } 137 | 138 | #wall > section > article > .experimental:before { 139 | vertical-align: bottom; 140 | content: url(imgs/phial.svg); 141 | position: absolute; 142 | top: 37px; left: 0px; 143 | background-color: #343838; 144 | height: 23px; 145 | border-radius: 5px; 146 | opacity: 0.5; 147 | } 148 | 149 | /* 150 | #wall > section > article > .hasademo:after { 151 | vertical-align: bottom; 152 | content: "D"; 153 | font-size: 20px; 154 | font-weight: bold; 155 | padding-left: 5px; 156 | color: white; 157 | position: absolute; 158 | top: 67px; left: 0px; 159 | background-color: #343838; 160 | height: 23px; 161 | border-radius: 5px; 162 | width: 20px; 163 | opacity: 0.5; 164 | } 165 | 166 | #wall > section > article > .hasademo:not(.experimental):after { 167 | top: 38px; 168 | } 169 | */ 170 | 171 | .demo .closebutton { 172 | position: absolute; 173 | top: -18px; 174 | right: -18px; 175 | cursor: pointer; 176 | } 177 | 178 | @media all and (max-width: 900px) { #wall { width: 600px; } #overview {display: none;}} 179 | @media all and (max-width: 600px) { #wall { width: 300px; } #wall > section > h1 { font-size: 70px; } } 180 | 181 | /* Mozilla Badge position override */ 182 | a#gobackbutton, a#gobackbutton:hover { top: 100px; } 183 | -------------------------------------------------------------------------------- /webgl_assets/glUtils.js: -------------------------------------------------------------------------------- 1 | // augment Sylvester some 2 | Matrix.Translation = function (v) 3 | { 4 | if (v.elements.length == 2) { 5 | var r = Matrix.I(3); 6 | r.elements[2][0] = v.elements[0]; 7 | r.elements[2][1] = v.elements[1]; 8 | return r; 9 | } 10 | 11 | if (v.elements.length == 3) { 12 | var r = Matrix.I(4); 13 | r.elements[0][3] = v.elements[0]; 14 | r.elements[1][3] = v.elements[1]; 15 | r.elements[2][3] = v.elements[2]; 16 | return r; 17 | } 18 | 19 | throw "Invalid length for Translation"; 20 | } 21 | 22 | Matrix.prototype.flatten = function () 23 | { 24 | var result = []; 25 | if (this.elements.length == 0) 26 | return []; 27 | 28 | 29 | for (var j = 0; j < this.elements[0].length; j++) 30 | for (var i = 0; i < this.elements.length; i++) 31 | result.push(this.elements[i][j]); 32 | return result; 33 | } 34 | 35 | Matrix.prototype.ensure4x4 = function() 36 | { 37 | if (this.elements.length == 4 && 38 | this.elements[0].length == 4) 39 | return this; 40 | 41 | if (this.elements.length > 4 || 42 | this.elements[0].length > 4) 43 | return null; 44 | 45 | for (var i = 0; i < this.elements.length; i++) { 46 | for (var j = this.elements[i].length; j < 4; j++) { 47 | if (i == j) 48 | this.elements[i].push(1); 49 | else 50 | this.elements[i].push(0); 51 | } 52 | } 53 | 54 | for (var i = this.elements.length; i < 4; i++) { 55 | if (i == 0) 56 | this.elements.push([1, 0, 0, 0]); 57 | else if (i == 1) 58 | this.elements.push([0, 1, 0, 0]); 59 | else if (i == 2) 60 | this.elements.push([0, 0, 1, 0]); 61 | else if (i == 3) 62 | this.elements.push([0, 0, 0, 1]); 63 | } 64 | 65 | return this; 66 | }; 67 | 68 | Matrix.prototype.make3x3 = function() 69 | { 70 | if (this.elements.length != 4 || 71 | this.elements[0].length != 4) 72 | return null; 73 | 74 | return Matrix.create([[this.elements[0][0], this.elements[0][1], this.elements[0][2]], 75 | [this.elements[1][0], this.elements[1][1], this.elements[1][2]], 76 | [this.elements[2][0], this.elements[2][1], this.elements[2][2]]]); 77 | }; 78 | 79 | Vector.prototype.flatten = function () 80 | { 81 | return this.elements; 82 | }; 83 | 84 | function mht(m) { 85 | var s = ""; 86 | if (m.length == 16) { 87 | for (var i = 0; i < 4; i++) { 88 | s += "[" + m[i*4+0].toFixed(4) + "," + m[i*4+1].toFixed(4) + "," + m[i*4+2].toFixed(4) + "," + m[i*4+3].toFixed(4) + "]
"; 89 | } 90 | } else if (m.length == 9) { 91 | for (var i = 0; i < 3; i++) { 92 | s += "[" + m[i*3+0].toFixed(4) + "," + m[i*3+1].toFixed(4) + "," + m[i*3+2].toFixed(4) + "]
"; 93 | } 94 | } else { 95 | return m.toString(); 96 | } 97 | return s; 98 | } 99 | 100 | // 101 | // gluLookAt 102 | // 103 | function makeLookAt(ex, ey, ez, 104 | cx, cy, cz, 105 | ux, uy, uz) 106 | { 107 | var eye = $V([ex, ey, ez]); 108 | var center = $V([cx, cy, cz]); 109 | var up = $V([ux, uy, uz]); 110 | 111 | var mag; 112 | 113 | var z = eye.subtract(center).toUnitVector(); 114 | var x = up.cross(z).toUnitVector(); 115 | var y = z.cross(x).toUnitVector(); 116 | 117 | var m = $M([[x.e(1), x.e(2), x.e(3), 0], 118 | [y.e(1), y.e(2), y.e(3), 0], 119 | [z.e(1), z.e(2), z.e(3), 0], 120 | [0, 0, 0, 1]]); 121 | 122 | var t = $M([[1, 0, 0, -ex], 123 | [0, 1, 0, -ey], 124 | [0, 0, 1, -ez], 125 | [0, 0, 0, 1]]); 126 | return m.x(t); 127 | } 128 | 129 | // 130 | // gluPerspective 131 | // 132 | function makePerspective(fovy, aspect, znear, zfar) 133 | { 134 | var ymax = znear * Math.tan(fovy * Math.PI / 360.0); 135 | var ymin = -ymax; 136 | var xmin = ymin * aspect; 137 | var xmax = ymax * aspect; 138 | 139 | return makeFrustum(xmin, xmax, ymin, ymax, znear, zfar); 140 | } 141 | 142 | // 143 | // glFrustum 144 | // 145 | function makeFrustum(left, right, 146 | bottom, top, 147 | znear, zfar) 148 | { 149 | var X = 2*znear/(right-left); 150 | var Y = 2*znear/(top-bottom); 151 | var A = (right+left)/(right-left); 152 | var B = (top+bottom)/(top-bottom); 153 | var C = -(zfar+znear)/(zfar-znear); 154 | var D = -2*zfar*znear/(zfar-znear); 155 | 156 | return $M([[X, 0, A, 0], 157 | [0, Y, B, 0], 158 | [0, 0, C, D], 159 | [0, 0, -1, 0]]); 160 | } 161 | 162 | // 163 | // glOrtho 164 | // 165 | function makeOrtho(left, right, bottom, top, znear, zfar) 166 | { 167 | var tx = - (right + left) / (right - left); 168 | var ty = - (top + bottom) / (top - bottom); 169 | var tz = - (zfar + znear) / (zfar - znear); 170 | 171 | return $M([[2 / (right - left), 0, 0, tx], 172 | [0, 2 / (top - bottom), 0, ty], 173 | [0, 0, -2 / (zfar - znear), tz], 174 | [0, 0, 0, 1]]); 175 | } 176 | -------------------------------------------------------------------------------- /webgl_assets/gldemo.js: -------------------------------------------------------------------------------- 1 | var per_fragment_lighting_fs = [ 2 | " #ifdef GL_ES", 3 | " precision highp float;", 4 | " #endif", 5 | " varying vec2 vTextureCoord;", 6 | " varying vec4 vTransformedNormal;", 7 | " varying vec4 vPosition;", 8 | " uniform float uMaterialShininess;", 9 | " uniform bool uShowSpecularHighlights;", 10 | " uniform bool uUseLighting;", 11 | " uniform bool uUseTextures;", 12 | " uniform vec3 uAmbientColor;", 13 | " uniform vec3 uPointLightingLocation;", 14 | " uniform vec3 uPointLightingSpecularColor;", 15 | " uniform vec3 uPointLightingDiffuseColor;", 16 | " uniform sampler2D uSampler;", 17 | " void main(void) {", 18 | " vec3 lightWeighting;", 19 | " if (!uUseLighting) {", 20 | " lightWeighting = vec3(1.0, 1.0, 1.0);", 21 | " } else {", 22 | " vec3 lightDirection = normalize(uPointLightingLocation - vPosition.xyz);", 23 | " vec3 normal = normalize(vTransformedNormal.xyz);", 24 | " float specularLightWeighting = 0.0;", 25 | " if (uShowSpecularHighlights) {", 26 | " vec3 eyeDirection = normalize(-vPosition.xyz);", 27 | " vec3 reflectionDirection = reflect(-lightDirection, normal);", 28 | " specularLightWeighting = pow(max(dot(reflectionDirection, eyeDirection), 0.0), uMaterialShininess);", 29 | " }", 30 | " float diffuseLightWeighting = max(dot(normal, lightDirection), 0.0);", 31 | " lightWeighting = uAmbientColor", 32 | " + uPointLightingSpecularColor * specularLightWeighting", 33 | " + uPointLightingDiffuseColor * diffuseLightWeighting;", 34 | " }", 35 | " vec4 fragmentColor;", 36 | " if (uUseTextures) {", 37 | " fragmentColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));", 38 | " } else {", 39 | " fragmentColor = vec4(1.0, 1.0, 1.0, 1.0);", 40 | " }", 41 | " gl_FragColor = vec4(fragmentColor.rgb * lightWeighting, fragmentColor.a);", 42 | " }" 43 | ].join("\n"); 44 | 45 | var per_fragment_lighting_vs = [ 46 | " attribute vec3 aVertexPosition;", 47 | " attribute vec3 aVertexNormal;", 48 | " attribute vec2 aTextureCoord;", 49 | " uniform mat4 uMVMatrix;", 50 | " uniform mat4 uPMatrix;", 51 | " uniform mat4 uNMatrix;", 52 | " varying vec2 vTextureCoord;", 53 | " varying vec4 vTransformedNormal;", 54 | " varying vec4 vPosition;", 55 | " void main(void) {", 56 | " vPosition = uMVMatrix * vec4(aVertexPosition, 1.0);", 57 | " gl_Position = uPMatrix * vPosition;", 58 | " vTextureCoord = aTextureCoord;", 59 | " vTransformedNormal = uNMatrix * vec4(aVertexNormal, 1.0);", 60 | " }", 61 | ].join("\n"); 62 | 63 | 64 | 65 | 66 | var gl; 67 | 68 | var nogl; 69 | 70 | function initGL(canvas) { 71 | try { 72 | gl = canvas.getContext("experimental-webgl"); 73 | gl.viewportWidth = canvas.width; 74 | gl.viewportHeight = canvas.height; 75 | nogl = false; 76 | } catch(e) {} 77 | if (!gl) { 78 | nogl = true; 79 | document.body.classList.add("nogl"); 80 | } 81 | } 82 | 83 | 84 | function getShader(gl, str, isFragment) { 85 | var shader; 86 | if (isFragment) { 87 | shader = gl.createShader(gl.FRAGMENT_SHADER); 88 | } else { 89 | shader = gl.createShader(gl.VERTEX_SHADER); 90 | } 91 | 92 | gl.shaderSource(shader, str); 93 | gl.compileShader(shader); 94 | 95 | if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { 96 | alert(gl.getShaderInfoLog(shader)); 97 | return null; 98 | } 99 | 100 | return shader; 101 | } 102 | 103 | 104 | var shaderProgram; 105 | function initShaders() { 106 | var fragmentShader = getShader(gl, per_fragment_lighting_fs, true); 107 | var vertexShader = getShader(gl, per_fragment_lighting_vs, false); 108 | 109 | shaderProgram = gl.createProgram(); 110 | gl.attachShader(shaderProgram, vertexShader); 111 | gl.attachShader(shaderProgram, fragmentShader); 112 | gl.linkProgram(shaderProgram); 113 | 114 | if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { 115 | alert("Could not initialise shaders"); 116 | } 117 | 118 | gl.useProgram(shaderProgram); 119 | 120 | shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); 121 | gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); 122 | 123 | shaderProgram.vertexNormalAttribute = gl.getAttribLocation(shaderProgram, "aVertexNormal"); 124 | gl.enableVertexAttribArray(shaderProgram.vertexNormalAttribute); 125 | 126 | shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); 127 | gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); 128 | 129 | shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix"); 130 | shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); 131 | shaderProgram.nMatrixUniform = gl.getUniformLocation(shaderProgram, "uNMatrix"); 132 | shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); 133 | shaderProgram.materialShininessUniform = gl.getUniformLocation(shaderProgram, "uMaterialShininess"); 134 | shaderProgram.showSpecularHighlightsUniform = gl.getUniformLocation(shaderProgram, "uShowSpecularHighlights"); 135 | shaderProgram.useTexturesUniform = gl.getUniformLocation(shaderProgram, "uUseTextures"); 136 | shaderProgram.useLightingUniform = gl.getUniformLocation(shaderProgram, "uUseLighting"); 137 | shaderProgram.ambientColorUniform = gl.getUniformLocation(shaderProgram, "uAmbientColor"); 138 | shaderProgram.pointLightingLocationUniform = gl.getUniformLocation(shaderProgram, "uPointLightingLocation"); 139 | shaderProgram.pointLightingSpecularColorUniform = gl.getUniformLocation(shaderProgram, "uPointLightingSpecularColor"); 140 | shaderProgram.pointLightingDiffuseColorUniform = gl.getUniformLocation(shaderProgram, "uPointLightingDiffuseColor"); 141 | } 142 | 143 | 144 | function handleLoadedTexture(texture) { 145 | gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); 146 | gl.bindTexture(gl.TEXTURE_2D, texture); 147 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.image); 148 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); 149 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST); 150 | gl.generateMipmap(gl.TEXTURE_2D); 151 | 152 | gl.bindTexture(gl.TEXTURE_2D, null); 153 | tick(); 154 | } 155 | 156 | 157 | var earthTexture; 158 | var galvanizedTexture; 159 | function initTextures() { 160 | earthTexture = gl.createTexture(); 161 | earthTexture.image = new Image(); 162 | earthTexture.image.onload = function() { 163 | handleLoadedTexture(earthTexture) 164 | } 165 | earthTexture.image.src = "earth.jpg"; 166 | 167 | galvanizedTexture = gl.createTexture(); 168 | galvanizedTexture.image = new Image(); 169 | galvanizedTexture.image.onload = function() { 170 | handleLoadedTexture(galvanizedTexture) 171 | } 172 | galvanizedTexture.image.src = "webgl_assets/texture.jpg"; 173 | } 174 | 175 | 176 | var mvMatrix; 177 | var mvMatrixStack = []; 178 | 179 | function mvPushMatrix(m) { 180 | if (m) { 181 | mvMatrixStack.push(m.dup()); 182 | mvMatrix = m.dup(); 183 | } else { 184 | mvMatrixStack.push(mvMatrix.dup()); 185 | } 186 | } 187 | 188 | function mvPopMatrix() { 189 | if (mvMatrixStack.length == 0) { 190 | throw "Invalid popMatrix!"; 191 | } 192 | mvMatrix = mvMatrixStack.pop(); 193 | return mvMatrix; 194 | } 195 | 196 | function loadIdentity() { 197 | mvMatrix = Matrix.I(4); 198 | } 199 | 200 | 201 | function multMatrix(m) { 202 | mvMatrix = mvMatrix.x(m); 203 | } 204 | 205 | 206 | function mvTranslate(v) { 207 | var m = Matrix.Translation($V([v[0], v[1], v[2]])).ensure4x4(); 208 | multMatrix(m); 209 | } 210 | 211 | 212 | function createRotationMatrix(angle, v) { 213 | var arad = angle * Math.PI / 180.0; 214 | return Matrix.Rotation(arad, $V([v[0], v[1], v[2]])).ensure4x4(); 215 | } 216 | 217 | 218 | function mvRotate(angle, v) { 219 | multMatrix(createRotationMatrix(angle, v)); 220 | } 221 | 222 | 223 | var pMatrix; 224 | function perspective(fovy, aspect, znear, zfar) { 225 | pMatrix = makePerspective(fovy, aspect, znear, zfar); 226 | } 227 | 228 | 229 | function setMatrixUniforms() { 230 | gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, new Float32Array(pMatrix.flatten())); 231 | gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, new Float32Array(mvMatrix.flatten())); 232 | 233 | var normalMatrix = mvMatrix.inverse(); 234 | normalMatrix = normalMatrix.transpose(); 235 | gl.uniformMatrix4fv(shaderProgram.nMatrixUniform, false, new Float32Array(normalMatrix.flatten())); 236 | } 237 | 238 | 239 | var teapotVertexPositionBuffer; 240 | var teapotVertexNormalBuffer; 241 | var teapotVertexTextureCoordBuffer; 242 | var teapotVertexIndexBuffer; 243 | function handleLoadedTeapot(teapotData) { 244 | teapotVertexNormalBuffer = gl.createBuffer(); 245 | gl.bindBuffer(gl.ARRAY_BUFFER, teapotVertexNormalBuffer); 246 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(teapotData.vertexNormals), gl.STATIC_DRAW); 247 | teapotVertexNormalBuffer.itemSize = 3; 248 | teapotVertexNormalBuffer.numItems = teapotData.vertexNormals.length / 3; 249 | 250 | teapotVertexTextureCoordBuffer = gl.createBuffer(); 251 | gl.bindBuffer(gl.ARRAY_BUFFER, teapotVertexTextureCoordBuffer); 252 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(teapotData.vertexTextureCoords), gl.STATIC_DRAW); 253 | teapotVertexTextureCoordBuffer.itemSize = 2; 254 | teapotVertexTextureCoordBuffer.numItems = teapotData.vertexTextureCoords.length / 2; 255 | 256 | teapotVertexPositionBuffer = gl.createBuffer(); 257 | gl.bindBuffer(gl.ARRAY_BUFFER, teapotVertexPositionBuffer); 258 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(teapotData.vertexPositions), gl.STATIC_DRAW); 259 | teapotVertexPositionBuffer.itemSize = 3; 260 | teapotVertexPositionBuffer.numItems = teapotData.vertexPositions.length / 3; 261 | 262 | teapotVertexIndexBuffer = gl.createBuffer(); 263 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, teapotVertexIndexBuffer); 264 | gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(teapotData.indices), gl.STREAM_DRAW); 265 | teapotVertexIndexBuffer.itemSize = 1; 266 | teapotVertexIndexBuffer.numItems = teapotData.indices.length; 267 | } 268 | 269 | 270 | function loadTeapot() { 271 | var request = new XMLHttpRequest(); 272 | request.open("GET", "webgl_assets/Teapot.json"); 273 | request.onreadystatechange = function() { 274 | if (request.readyState == 4) { 275 | handleLoadedTeapot(JSON.parse(request.responseText)); 276 | } 277 | } 278 | request.send(); 279 | } 280 | 281 | 282 | var teapotAngle = 180; 283 | 284 | function drawScene() { 285 | gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight); 286 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 287 | 288 | if (teapotVertexPositionBuffer == null || teapotVertexNormalBuffer == null || teapotVertexTextureCoordBuffer == null || teapotVertexIndexBuffer == null) { 289 | return; 290 | } 291 | 292 | perspective(50, gl.viewportWidth / gl.viewportHeight, 0.1, 100.0); 293 | 294 | var specularHighlights = true; 295 | gl.uniform1i(shaderProgram.showSpecularHighlightsUniform, specularHighlights); 296 | 297 | var lighting = true; 298 | gl.uniform1i(shaderProgram.useLightingUniform, lighting); 299 | if (lighting) { 300 | gl.uniform3f(shaderProgram.ambientColorUniform, 0.2, 0.2, 0.2); 301 | gl.uniform3f(shaderProgram.pointLightingLocationUniform, -10, 4, -20); 302 | gl.uniform3f(shaderProgram.pointLightingSpecularColorUniform, 0.8, 0.8, 0.8); 303 | gl.uniform3f(shaderProgram.pointLightingDiffuseColorUniform, 0.8, 0.8, 0.8); 304 | } 305 | 306 | var texture = "galvanized"; 307 | gl.uniform1i(shaderProgram.useTexturesUniform, texture != "none"); 308 | 309 | loadIdentity(); 310 | 311 | mvTranslate([0, 0, -30]); 312 | mvRotate(23.4, [1, 0, -1]); 313 | mvRotate(teapotAngle, [0, 1, 0]); 314 | gl.activeTexture(gl.TEXTURE0); 315 | if (texture == "earth") { 316 | gl.bindTexture(gl.TEXTURE_2D, earthTexture); 317 | } else if (texture == "galvanized") { 318 | gl.bindTexture(gl.TEXTURE_2D, galvanizedTexture); 319 | } 320 | gl.uniform1i(shaderProgram.samplerUniform, 0); 321 | 322 | gl.uniform1f(shaderProgram.materialShininessUniform, 12); 323 | 324 | gl.bindBuffer(gl.ARRAY_BUFFER, teapotVertexPositionBuffer); 325 | gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, teapotVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0); 326 | 327 | gl.bindBuffer(gl.ARRAY_BUFFER, teapotVertexTextureCoordBuffer); 328 | gl.vertexAttribPointer(shaderProgram.textureCoordAttribute, teapotVertexTextureCoordBuffer.itemSize, gl.FLOAT, false, 0, 0); 329 | 330 | gl.bindBuffer(gl.ARRAY_BUFFER, teapotVertexNormalBuffer); 331 | gl.vertexAttribPointer(shaderProgram.vertexNormalAttribute, teapotVertexNormalBuffer.itemSize, gl.FLOAT, false, 0, 0); 332 | 333 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, teapotVertexIndexBuffer); 334 | setMatrixUniforms(); 335 | gl.drawElements(gl.TRIANGLES, teapotVertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0); 336 | } 337 | 338 | 339 | 340 | var teapotRotating = false; 341 | 342 | function tick() { 343 | drawScene(); 344 | teapotAngle += 1.5; 345 | if (teapotRotating) setTimeout(tick, 60); 346 | } 347 | 348 | 349 | function webGLStart() { 350 | var canvas = document.querySelector("#webgldemo canvas"); 351 | initGL(canvas); 352 | initShaders(); 353 | initTextures(); 354 | loadTeapot(); 355 | 356 | gl.clearColor(0.0, 0.0, 0.0, 0.0); 357 | 358 | gl.clearDepth(1.0); 359 | 360 | gl.enable(gl.DEPTH_TEST); 361 | gl.depthFunc(gl.LEQUAL); 362 | tick(); 363 | } 364 | 365 | window.addEventListener("load", webGLStart, true); 366 | 367 | function startTeaPot() { 368 | if (nogl) return; 369 | teapotRotating = true; tick(); 370 | } 371 | function stopTeaPot() { 372 | if (nogl) return; 373 | teapotRotating = false; 374 | } 375 | -------------------------------------------------------------------------------- /webgl_assets/sylvester.js: -------------------------------------------------------------------------------- 1 | eval(function(p,a,c,k,e,r){e=function(c){return(c35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('9 17={3i:\'0.1.3\',16:1e-6};l v(){}v.23={e:l(i){8(i<1||i>7.4.q)?w:7.4[i-1]},2R:l(){8 7.4.q},1u:l(){8 F.1x(7.2u(7))},24:l(a){9 n=7.4.q;9 V=a.4||a;o(n!=V.q){8 1L}J{o(F.13(7.4[n-1]-V[n-1])>17.16){8 1L}}H(--n);8 2x},1q:l(){8 v.u(7.4)},1b:l(a){9 b=[];7.28(l(x,i){b.19(a(x,i))});8 v.u(b)},28:l(a){9 n=7.4.q,k=n,i;J{i=k-n;a(7.4[i],i+1)}H(--n)},2q:l(){9 r=7.1u();o(r===0){8 7.1q()}8 7.1b(l(x){8 x/r})},1C:l(a){9 V=a.4||a;9 n=7.4.q,k=n,i;o(n!=V.q){8 w}9 b=0,1D=0,1F=0;7.28(l(x,i){b+=x*V[i-1];1D+=x*x;1F+=V[i-1]*V[i-1]});1D=F.1x(1D);1F=F.1x(1F);o(1D*1F===0){8 w}9 c=b/(1D*1F);o(c<-1){c=-1}o(c>1){c=1}8 F.37(c)},1m:l(a){9 b=7.1C(a);8(b===w)?w:(b<=17.16)},34:l(a){9 b=7.1C(a);8(b===w)?w:(F.13(b-F.1A)<=17.16)},2k:l(a){9 b=7.2u(a);8(b===w)?w:(F.13(b)<=17.16)},2j:l(a){9 V=a.4||a;o(7.4.q!=V.q){8 w}8 7.1b(l(x,i){8 x+V[i-1]})},2C:l(a){9 V=a.4||a;o(7.4.q!=V.q){8 w}8 7.1b(l(x,i){8 x-V[i-1]})},22:l(k){8 7.1b(l(x){8 x*k})},x:l(k){8 7.22(k)},2u:l(a){9 V=a.4||a;9 i,2g=0,n=7.4.q;o(n!=V.q){8 w}J{2g+=7.4[n-1]*V[n-1]}H(--n);8 2g},2f:l(a){9 B=a.4||a;o(7.4.q!=3||B.q!=3){8 w}9 A=7.4;8 v.u([(A[1]*B[2])-(A[2]*B[1]),(A[2]*B[0])-(A[0]*B[2]),(A[0]*B[1])-(A[1]*B[0])])},2A:l(){9 m=0,n=7.4.q,k=n,i;J{i=k-n;o(F.13(7.4[i])>F.13(m)){m=7.4[i]}}H(--n);8 m},2Z:l(x){9 a=w,n=7.4.q,k=n,i;J{i=k-n;o(a===w&&7.4[i]==x){a=i+1}}H(--n);8 a},3g:l(){8 S.2X(7.4)},2d:l(){8 7.1b(l(x){8 F.2d(x)})},2V:l(x){8 7.1b(l(y){8(F.13(y-x)<=17.16)?x:y})},1o:l(a){o(a.K){8 a.1o(7)}9 V=a.4||a;o(V.q!=7.4.q){8 w}9 b=0,2b;7.28(l(x,i){2b=x-V[i-1];b+=2b*2b});8 F.1x(b)},3a:l(a){8 a.1h(7)},2T:l(a){8 a.1h(7)},1V:l(t,a){9 V,R,x,y,z;2S(7.4.q){27 2:V=a.4||a;o(V.q!=2){8 w}R=S.1R(t).4;x=7.4[0]-V[0];y=7.4[1]-V[1];8 v.u([V[0]+R[0][0]*x+R[0][1]*y,V[1]+R[1][0]*x+R[1][1]*y]);1I;27 3:o(!a.U){8 w}9 C=a.1r(7).4;R=S.1R(t,a.U).4;x=7.4[0]-C[0];y=7.4[1]-C[1];z=7.4[2]-C[2];8 v.u([C[0]+R[0][0]*x+R[0][1]*y+R[0][2]*z,C[1]+R[1][0]*x+R[1][1]*y+R[1][2]*z,C[2]+R[2][0]*x+R[2][1]*y+R[2][2]*z]);1I;2P:8 w}},1t:l(a){o(a.K){9 P=7.4.2O();9 C=a.1r(P).4;8 v.u([C[0]+(C[0]-P[0]),C[1]+(C[1]-P[1]),C[2]+(C[2]-(P[2]||0))])}1d{9 Q=a.4||a;o(7.4.q!=Q.q){8 w}8 7.1b(l(x,i){8 Q[i-1]+(Q[i-1]-x)})}},1N:l(){9 V=7.1q();2S(V.4.q){27 3:1I;27 2:V.4.19(0);1I;2P:8 w}8 V},2n:l(){8\'[\'+7.4.2K(\', \')+\']\'},26:l(a){7.4=(a.4||a).2O();8 7}};v.u=l(a){9 V=25 v();8 V.26(a)};v.i=v.u([1,0,0]);v.j=v.u([0,1,0]);v.k=v.u([0,0,1]);v.2J=l(n){9 a=[];J{a.19(F.2F())}H(--n);8 v.u(a)};v.1j=l(n){9 a=[];J{a.19(0)}H(--n);8 v.u(a)};l S(){}S.23={e:l(i,j){o(i<1||i>7.4.q||j<1||j>7.4[0].q){8 w}8 7.4[i-1][j-1]},33:l(i){o(i>7.4.q){8 w}8 v.u(7.4[i-1])},2E:l(j){o(j>7.4[0].q){8 w}9 a=[],n=7.4.q,k=n,i;J{i=k-n;a.19(7.4[i][j-1])}H(--n);8 v.u(a)},2R:l(){8{2D:7.4.q,1p:7.4[0].q}},2D:l(){8 7.4.q},1p:l(){8 7.4[0].q},24:l(a){9 M=a.4||a;o(1g(M[0][0])==\'1f\'){M=S.u(M).4}o(7.4.q!=M.q||7.4[0].q!=M[0].q){8 1L}9 b=7.4.q,15=b,i,G,10=7.4[0].q,j;J{i=15-b;G=10;J{j=10-G;o(F.13(7.4[i][j]-M[i][j])>17.16){8 1L}}H(--G)}H(--b);8 2x},1q:l(){8 S.u(7.4)},1b:l(a){9 b=[],12=7.4.q,15=12,i,G,10=7.4[0].q,j;J{i=15-12;G=10;b[i]=[];J{j=10-G;b[i][j]=a(7.4[i][j],i+1,j+1)}H(--G)}H(--12);8 S.u(b)},2i:l(a){9 M=a.4||a;o(1g(M[0][0])==\'1f\'){M=S.u(M).4}8(7.4.q==M.q&&7.4[0].q==M[0].q)},2j:l(a){9 M=a.4||a;o(1g(M[0][0])==\'1f\'){M=S.u(M).4}o(!7.2i(M)){8 w}8 7.1b(l(x,i,j){8 x+M[i-1][j-1]})},2C:l(a){9 M=a.4||a;o(1g(M[0][0])==\'1f\'){M=S.u(M).4}o(!7.2i(M)){8 w}8 7.1b(l(x,i,j){8 x-M[i-1][j-1]})},2B:l(a){9 M=a.4||a;o(1g(M[0][0])==\'1f\'){M=S.u(M).4}8(7.4[0].q==M.q)},22:l(a){o(!a.4){8 7.1b(l(x){8 x*a})}9 b=a.1u?2x:1L;9 M=a.4||a;o(1g(M[0][0])==\'1f\'){M=S.u(M).4}o(!7.2B(M)){8 w}9 d=7.4.q,15=d,i,G,10=M[0].q,j;9 e=7.4[0].q,4=[],21,20,c;J{i=15-d;4[i]=[];G=10;J{j=10-G;21=0;20=e;J{c=e-20;21+=7.4[i][c]*M[c][j]}H(--20);4[i][j]=21}H(--G)}H(--d);9 M=S.u(4);8 b?M.2E(1):M},x:l(a){8 7.22(a)},32:l(a,b,c,d){9 e=[],12=c,i,G,j;9 f=7.4.q,1p=7.4[0].q;J{i=c-12;e[i]=[];G=d;J{j=d-G;e[i][j]=7.4[(a+i-1)%f][(b+j-1)%1p]}H(--G)}H(--12);8 S.u(e)},31:l(){9 a=7.4.q,1p=7.4[0].q;9 b=[],12=1p,i,G,j;J{i=1p-12;b[i]=[];G=a;J{j=a-G;b[i][j]=7.4[j][i]}H(--G)}H(--12);8 S.u(b)},1y:l(){8(7.4.q==7.4[0].q)},2A:l(){9 m=0,12=7.4.q,15=12,i,G,10=7.4[0].q,j;J{i=15-12;G=10;J{j=10-G;o(F.13(7.4[i][j])>F.13(m)){m=7.4[i][j]}}H(--G)}H(--12);8 m},2Z:l(x){9 a=w,12=7.4.q,15=12,i,G,10=7.4[0].q,j;J{i=15-12;G=10;J{j=10-G;o(7.4[i][j]==x){8{i:i+1,j:j+1}}}H(--G)}H(--12);8 w},30:l(){o(!7.1y){8 w}9 a=[],n=7.4.q,k=n,i;J{i=k-n;a.19(7.4[i][i])}H(--n);8 v.u(a)},1K:l(){9 M=7.1q(),1c;9 n=7.4.q,k=n,i,1s,1n=7.4[0].q,p;J{i=k-n;o(M.4[i][i]==0){2e(j=i+1;j17.16){1Y++;1I}}H(--G)}H(--a);8 1Y},3d:l(){8 7.1Y()},2W:l(a){9 M=a.4||a;o(1g(M[0][0])==\'1f\'){M=S.u(M).4}9 T=7.1q(),1p=T.4[0].q;9 b=T.4.q,15=b,i,G,10=M[0].q,j;o(b!=M.q){8 w}J{i=15-b;G=10;J{j=10-G;T.4[i][1p+j]=M[i][j]}H(--G)}H(--b);8 T},2w:l(){o(!7.1y()||7.2y()){8 w}9 a=7.4.q,15=a,i,j;9 M=7.2W(S.I(a)).1K();9 b,1n=M.4[0].q,p,1c,2v;9 c=[],2c;J{i=a-1;1c=[];b=1n;c[i]=[];2v=M.4[i][i];J{p=1n-b;2c=M.4[i][p]/2v;1c.19(2c);o(p>=15){c[i].19(2c)}}H(--b);M.4[i]=1c;2e(j=0;j3||b.4.q>3){8 w}9 c=b.1u();o(c===0){8 w}7.K=a;7.U=v.u([b.4[0]/c,b.4[1]/c,b.4[2]/c]);8 7}};14.u=l(a,b){9 L=25 14();8 L.1Z(a,b)};14.X=14.u(v.1j(3),v.i);14.Y=14.u(v.1j(3),v.j);14.Z=14.u(v.1j(3),v.k);l 11(){}11.23={24:l(a){8(7.1h(a.K)&&7.1m(a))},1q:l(){8 11.u(7.K,7.W)},2U:l(a){9 V=a.4||a;8 11.u([7.K.4[0]+V[0],7.K.4[1]+V[1],7.K.4[2]+(V[2]||0)],7.W)},1m:l(a){9 b;o(a.W){b=7.W.1C(a.W);8(F.13(b)<=17.16||F.13(F.1A-b)<=17.16)}1d o(a.U){8 7.W.2k(a.U)}8 w},2k:l(a){9 b=7.W.1C(a.W);8(F.13(F.1A/2-b)<=17.16)},1o:l(a){o(7.1v(a)||7.1h(a)){8 0}o(a.K){9 A=7.K.4,B=a.K.4,N=7.W.4;8 F.13((A[0]-B[0])*N[0]+(A[1]-B[1])*N[1]+(A[2]-B[2])*N[2])}1d{9 P=a.4||a;9 A=7.K.4,N=7.W.4;8 F.13((A[0]-P[0])*N[0]+(A[1]-P[1])*N[1]+(A[2]-(P[2]||0))*N[2])}},1h:l(a){o(a.W){8 w}o(a.U){8(7.1h(a.K)&&7.1h(a.K.2j(a.U)))}1d{9 P=a.4||a;9 A=7.K.4,N=7.W.4;9 b=F.13(N[0]*(A[0]-P[0])+N[1]*(A[1]-P[1])+N[2]*(A[2]-(P[2]||0)));8(b<=17.16)}},1v:l(a){o(1g(a.U)==\'1f\'&&1g(a.W)==\'1f\'){8 w}8!7.1m(a)},1U:l(a){o(!7.1v(a)){8 w}o(a.U){9 A=a.K.4,D=a.U.4,P=7.K.4,N=7.W.4;9 b=(N[0]*(P[0]-A[0])+N[1]*(P[1]-A[1])+N[2]*(P[2]-A[2]))/(N[0]*D[0]+N[1]*D[1]+N[2]*D[2]);8 v.u([A[0]+D[0]*b,A[1]+D[1]*b,A[2]+D[2]*b])}1d o(a.W){9 c=7.W.2f(a.W).2q();9 N=7.W.4,A=7.K.4,O=a.W.4,B=a.K.4;9 d=S.1j(2,2),i=0;H(d.2y()){i++;d=S.u([[N[i%3],N[(i+1)%3]],[O[i%3],O[(i+1)%3]]])}9 e=d.2w().4;9 x=N[0]*A[0]+N[1]*A[1]+N[2]*A[2];9 y=O[0]*B[0]+O[1]*B[1]+O[2]*B[2];9 f=[e[0][0]*x+e[0][1]*y,e[1][0]*x+e[1][1]*y];9 g=[];2e(9 j=1;j<=3;j++){g.19((i==j)?0:f[(j+(5-i)%3)%3])}8 14.u(g,c)}},1r:l(a){9 P=a.4||a;9 A=7.K.4,N=7.W.4;9 b=(A[0]-P[0])*N[0]+(A[1]-P[1])*N[1]+(A[2]-(P[2]||0))*N[2];8 v.u([P[0]+N[0]*b,P[1]+N[1]*b,(P[2]||0)+N[2]*b])},1V:l(t,a){9 R=S.1R(t,a.U).4;9 C=a.1r(7.K).4;9 A=7.K.4,N=7.W.4;9 b=C[0],1E=C[1],1J=C[2],1w=A[0],18=A[1],1a=A[2];9 x=1w-b,y=18-1E,z=1a-1J;8 11.u([b+R[0][0]*x+R[0][1]*y+R[0][2]*z,1E+R[1][0]*x+R[1][1]*y+R[1][2]*z,1J+R[2][0]*x+R[2][1]*y+R[2][2]*z],[R[0][0]*N[0]+R[0][1]*N[1]+R[0][2]*N[2],R[1][0]*N[0]+R[1][1]*N[1]+R[1][2]*N[2],R[2][0]*N[0]+R[2][1]*N[1]+R[2][2]*N[2]])},1t:l(a){o(a.W){9 A=7.K.4,N=7.W.4;9 b=A[0],18=A[1],1a=A[2],2M=N[0],2L=N[1],2Q=N[2];9 c=7.K.1t(a).4;9 d=b+2M,2p=18+2L,2m=1a+2Q;9 Q=a.1r([d,2p,2m]).4;9 e=[Q[0]+(Q[0]-d)-c[0],Q[1]+(Q[1]-2p)-c[1],Q[2]+(Q[2]-2m)-c[2]];8 11.u(c,e)}1d o(a.U){8 7.1V(F.1A,a)}1d{9 P=a.4||a;8 11.u(7.K.1t([P[0],P[1],(P[2]||0)]),7.W)}},1Z:l(a,b,c){a=v.u(a);a=a.1N();o(a===w){8 w}b=v.u(b);b=b.1N();o(b===w){8 w}o(1g(c)==\'1f\'){c=w}1d{c=v.u(c);c=c.1N();o(c===w){8 w}}9 d=a.4[0],18=a.4[1],1a=a.4[2];9 e=b.4[0],1W=b.4[1],1X=b.4[2];9 f,1i;o(c!==w){9 g=c.4[0],2l=c.4[1],2t=c.4[2];f=v.u([(1W-18)*(2t-1a)-(1X-1a)*(2l-18),(1X-1a)*(g-d)-(e-d)*(2t-1a),(e-d)*(2l-18)-(1W-18)*(g-d)]);1i=f.1u();o(1i===0){8 w}f=v.u([f.4[0]/1i,f.4[1]/1i,f.4[2]/1i])}1d{1i=F.1x(e*e+1W*1W+1X*1X);o(1i===0){8 w}f=v.u([b.4[0]/1i,b.4[1]/1i,b.4[2]/1i])}7.K=a;7.W=f;8 7}};11.u=l(a,b,c){9 P=25 11();8 P.1Z(a,b,c)};11.2I=11.u(v.1j(3),v.k);11.2H=11.u(v.1j(3),v.i);11.2G=11.u(v.1j(3),v.j);11.36=11.2I;11.35=11.2H;11.3j=11.2G;9 $V=v.u;9 $M=S.u;9 $L=14.u;9 $P=11.u;',62,206,'||||elements|||this|return|var||||||||||||function|||if||length||||create|Vector|null|||||||||Math|nj|while||do|anchor||||||||Matrix||direction||normal||||kj|Plane|ni|abs|Line|ki|precision|Sylvester|A2|push|A3|map|els|else||undefined|typeof|contains|mod|Zero|D3|D2|isParallelTo|kp|distanceFrom|cols|dup|pointClosestTo|np|reflectionIn|modulus|intersects|A1|sqrt|isSquare|X2|PI|X3|angleFrom|mod1|C2|mod2|sin|cos|break|C3|toRightTriangular|false|Y3|to3D|E2|E1|E3|Rotation|Y2|Y1|intersectionWith|rotate|v12|v13|rank|setVectors|nc|sum|multiply|prototype|eql|new|setElements|case|each|PA3|PA2|part|new_element|round|for|cross|product|AD2|isSameSizeAs|add|isPerpendicularTo|v22|AN3|inspect|AD3|AN2|toUnitVector|PsubQ3|PsubQ2|v23|dot|divisor|inverse|true|isSingular|determinant|max|canMultiplyFromLeft|subtract|rows|col|random|ZX|YZ|XY|Random|join|N2|N1|D1|slice|default|N3|dimensions|switch|liesIn|translate|snapTo|augment|Diagonal|trace|indexOf|diagonal|transpose|minor|row|isAntiparallelTo|ZY|YX|acos|RotationZ|RotationY|liesOn|RotationX|inv|rk|tr|det|toDiagonalMatrix|toUpperTriangular|version|XZ'.split('|'),0,{})) -------------------------------------------------------------------------------- /webgl_assets/texture.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulrouget/html5dashboard/6fe0865bf493597bb3b788fce712c3a9d051ebcb/webgl_assets/texture.jpg --------------------------------------------------------------------------------