1 // ---
  2 // Copyright (c) 2010 Francesco Cottone, http://www.kesiev.com/
  3 // ---
  4 
  5 /**
  6  * @namespace Help module provides some Javascript-specific functions, such object copying, randomizing functions, 
  7  * string/array handlers and the akihabaraInit function.
  8  */
  9 var help={
 10 
 11  /**
 12  * Generates numbers from st to ed, along with a skip value.
 13  * @param {Integer} st Starting number.
 14  * @param {Integer} ed Ending number.
 15  * @param {Integer} skip Number to increment by.
 16  * @returns An array containing the set of numbers from st to ed, incrementing by skip.
 17  */
 18 	seq:function(st,ed,skip) {
 19 		var ret=[];
 20 		for (var i=st;i<ed;i+=(skip==null?1:skip)) ret.push(i);
 21 		return ret;
 22 	},
 23 
 24  /**
 25  * Multiplies two numbers together, returning the result, unless the first parameter is less than 2, in which case it returns 1.
 26  * @param {Float} v First value.
 27  * @param {Float} mul Second value.
 28  * @returns An integer, v*mul, unless v<2 in which case it returns 1.
 29  */
 30 	// Handle a multiplier like counter. that means, 0=1 / 1=1 / 2=2*mul etc...
 31 	multiplier:function(v,mul) {
 32 		return (!v||(v<2)?1:v*(!mul?1:mul));
 33 	},
 34 	
 35  /**
 36  * Prepends a string with repeated instances of another string until it the result is greater than or equal to a desired length.
 37  * @param {String} str The string you wish to modify.
 38  * @param {Integer} len The desired length of your resultant string.
 39  * @param {String} pad The string you wish to prepend to str.
 40  * @returns A string whose length is no greater than len+pad.length, with pad prepending str repeatedly.
 41  */
 42 	prepad:function(str,len,pad) {
 43 		str+="";
 44 		while (str.length<len) str=pad+str;
 45 		return str;
 46 	},
 47 	
 48  /**
 49  * Postpends a string with repeated instances of another string until it the result is greater than or equal to a desired length.
 50  * @param {String} str The string you wish to modify.
 51  * @param {Integer} len The desired length of your resultant string.
 52  * @param {String} pad The string you wish to postpend to str.
 53  * @returns A string whose length is no greater than len+pad.length, with pad postpending str repeatedly.
 54  */
 55 	postpad:function(str,len,pad) {
 56 		str+="";
 57 		while (str.length<len) str+=pad;
 58 		return str;
 59 	},
 60 
 61  /**
 62  * Tests to see if an object is being "jumped on" by another object. Only works for platformers, since it assumes accy>0 means you're falling onto something else.
 63  * @param {Object} th The object that is (possibly) being jumped on.
 64  * @param {Object} by The object doing the jumping-on.
 65  * @returns True if the two objects are overlapping enough and by.accy>0.
 66  */
 67 	isSquished:function(th,by) {
 68 		return ((by.accy>0)&&gbox.collides(th,by)&&(Math.abs(th.y-(by.y+by.h))<(th.h/2)))
 69 	},
 70  
 71  /**
 72  * Generates uniformly distributed random integers between min and min+range, non-inclusive. So help.random(0,2) will only return 0 and 1, etc.
 73  * @param {Integer} min The minimum random value to be returned by the function.
 74  * @param {Integer} range The number of different values returned by the function.
 75  * @returns An integer between min (includive) and min+range (noninclusive).
 76  */
 77 	random:function(min,range) {
 78 		return min+Math.floor(Math.random()*range);
 79 	},
 80  
 81  
 82   /**
 83  * Determines which frame of a given animation to display. Will loop an animation.
 84  * @param {Integer} cnt A global frame counter.
 85  * @param {Object} anim An object with parameters speed (the animation speed) and frames (the array representing the animation sequence).
 86  * @returns The particular animation frame to display during this step.
 87  */
 88 	decideFrame:function(cnt,anim) {
 89 		return anim.frames[Math.floor(cnt/anim.speed)%anim.frames.length];
 90 	},
 91   
 92  /**
 93  * Determines which frame of a given animation to display. Will remain on the last frame when the animation has played once.
 94  * @param {Integer} cnt A global frame counter.
 95  * @param {Object} anim An object with parameters speed (the animation speed) and frames (the array representing the animation sequence).
 96  * @returns The particular animation frame to display during this step.
 97  */
 98 	decideFrameOnce:function(cnt,anim) {
 99 		return anim.frames[(cnt>=anim.frames.length*anim.speed?anim.frames.length-1:Math.floor(cnt/anim.speed))];
100 	},
101   
102  /**
103  * Returns whether the animation was fully played at least once with decideFrame or fully with decideFrameOnce.
104  * @param {Integer} cnt A global frame counter.
105  * @param {Object} anim An object with parameters speed (the animation speed) and frames (the array representing the animation sequence).
106  * @returns A boolean, true if the animation has been played at least once.
107  */
108  	isLastFrameOnce:function(cnt,anim) {
109 		return (cnt>=anim.frames.length*anim.speed);
110 	},
111 
112  /**
113  * Given an incrementing value each step, this will return a value increasing from 0 until max/2, at which point it will decrement to 0, then go back up to max/2, in an endless cycle.
114  * @param {Integer} counter A counter.
115  * @param {Integer} max This determines the period of the function -- assuming counter is incrementing by one, a complete back-and-forth will take 'max' steps.
116  * @returns An integer.
117  */
118 	upAndDown:function(counter,max) {
119 		if ((counter%max)>(max/2)) return max-(counter%max); else return (counter%max);
120 	},
121   
122  /**
123  * Given x,y coordinates and map information, this returns the tile at a given point.
124  * @param {Integer} x An x-coordinate.
125  * @param {Integer} y A y-coordinate.
126  * @param {Object} map The map object.
127  * @param {Object} ifout An object or value to be returned if the x,y coordinate pair is outside the map.
128  * @param {String} mapid The id for the map array within the map object. Default is 'map'.
129  * @returns An integer representing the value of the tile in the map array at that x,y coordinate. If there is no tile, null is returned.
130  */
131  	getTileInMap:function(x,y,map,ifout,mapid) {
132 		if (!mapid) mapid="map";
133 		var ts=gbox._tiles[map.tileset];
134 		var tx=Math.floor(x/ts.tilew);
135 		var ty=Math.floor(y/ts.tileh);
136 		if ((ty<0)||(ty>=map[mapid].length)) return ifout; else
137 		if ((tx<0)||(tx>=map[mapid][ty].length)) return ifout; else
138 		return map[mapid][ty][tx];
139 	},
140 
141  /**
142  * Takes an ascii-art-style array of characters and converts it to an Akihabara-compatible map format.
143  * @param {Array} map An array of characters representing a map.
144  * @param {Array} tra A translation array. This is an array of arrays, formatted like [ [null, char1], [0, char2], [1, char3] ]. There must at least be a null entry, followed by one numerical entry for each tile type you want to render, corresponding to the unique characters in the map array. The null entry maps a character to empty space.
145  * @returns A map array formatted such that it can be attached to a map object.
146  */
147 	asciiArtToMap:function(map,tra) {
148 		var sz=tra[0][1].length;
149 		var ret=[];
150 		var xpos;
151 		var pie;
152 		for (var y=0;y<map.length;y++) {
153 			var row=[];
154 			xpos=0;
155 			while (xpos<map[y].length) {
156 				pie=map[y].substr(xpos,sz);
157 				for (var t=0;t<tra.length;t++)
158 					if (pie==tra[t][1]) {
159 						if (t==0) row.push(null); else row.push(tra[t][0]);
160 						break;
161 					}
162 				xpos+=sz;
163 			}
164 			ret.push(row);
165 		}
166 		return ret;
167 	},
168 	
169   /**
170   * Calculates and sets the width and height (map.h, map.w) and half-width and half-height (map.hh, map.hw) of a map object.
171   * @param {Object} map A map object, containing a map array and a tileset array.
172   * @returns A map object with map.w, map.h, map.hh, and map.hw set correctly.
173   */	
174 	// Finalize a map definition, setting height and width in pixels etc.
175 	// Args: (map)
176 	// Outs: finalized map
177 	finalizeTilemap:function(map) {
178 		var ts=gbox._tiles[map.tileset];
179 		map.h=map.map.length*ts.tileh;
180 		map.w=map.map[0].length*ts.tilew;
181 		map.hw=Math.floor(map.w/2);
182 		map.hh=Math.floor(map.h/2);
183 		return map;
184 	},
185  
186   /**
187   * Converts an x-coordinate of a pixel to its corresponding tile x-coordinate.
188   * @param {Object} map A map object, containing a map array and a tileset array.
189   * @param {Integer} x An x-coordinate.
190   * @param {Integer} gap (Not used.)
191   * @returns A map object with map.w, map.h, map.hh, and map.hw set correctly.
192   */	
193 	xPixelToTileX:function(map,x,gap) {
194 		var ts=gbox._tiles[map.tileset];
195 		return Math.floor(x/ts.tilew);
196 	},
197   
198   /**
199   * Converts a y-coordinate of a pixel to its corresponding tile y-coordinate.
200   * @param {Object} map A map object, containing a map array and a tileset array.
201   * @param {Integer} y A y-coordinate.
202   * @param {Integer} gap (Not used.)
203   * @returns A map object with map.w, map.h, map.hh, and map.hw set correctly.
204   */	
205 	yPixelToTileY:function(map,y,gap) {
206 		var ts=gbox._tiles[map.tileset];
207 		return Math.floor(y/ts.tileh);
208 	},
209   
210   /**
211   * Converts an x-coordinate of a pixel to the x-coordinate of the tile column it's in. This effectively "snaps" an x coordinate to a tile edge.
212   * @param {Object} map A map object, containing a map array and a tileset array.
213   * @param {Integer} x An x-coordinate.
214   * @param {Integer} gap Number of pixels gap in tilemap. Default is 0.
215   * @returns The x-coordinate in pixels of the tile column.
216   */	
217 	xPixelToTile:function(map,x,gap) {
218 		var ts=gbox._tiles[map.tileset];
219 		return (Math.floor(x/ts.tilew)+(gap?gap:0))*ts.tilew;
220 	},
221 
222   /**
223   * Converts a y-coordinate of a pixel to the y-coordinate of the tile row it's in. This effectively "snaps" a y coordinate to a tile edge.
224   * @param {Object} map A map object, containing a map array and a tileset array.
225   * @param {Integer} y A y-coordinate.
226   * @param {Integer} gap Number of pixels gap in tilemap. Default is 0.
227   * @returns The y-coordinate in pixels of the tile row.
228   */	
229 	yPixelToTile:function(map,y,gap) {
230 		var ts=gbox._tiles[map.tileset];
231 		return (Math.floor(y/ts.tileh)+(gap?gap:0))*ts.tileh;
232 	},
233   
234   /**
235   * Limits a number to a certain range. If the number is below the minimum, the minimum is returned. If the number is above the maximum, the maximum is returned.
236   * @param {Float} v A value.
237   * @param {Float} min The minimum limit.
238   * @param {Float} max The maximum limit.
239   * @returns A value equal to v if min<v<max. Returns min if v<min, max if v>max.
240   */	
241 	limit:function(v,min,max) {
242 		if (v<min) return min; else if (v>max) return max; else return v;
243 	},
244  
245   /**
246   * Subtracts or adds 1 to a value, always converging to zero. For example, passing -3 yields -2, 5 yields 4, etc. Works best with integers.
247   * @param {Integer} v A value.
248   * @returns A value that is one closer to 0 on the number line than v.
249   */	
250 	goToZero:function(v) { return (v?v-(v/Math.abs(v)):0); },
251 	
252   /**
253   * Merges two sets of parameters together without overwriting existing parameters. This merges from model to data, and if data and model share parameters, data's values remain intact.
254   * @param {Object} data An object containing a set of parameters, the destination of the merge.
255   * @param {Object} model An object containing a set of parameters, the source of the merge.
256   * @returns A merged model where the values of 'data' remain untouched: only new parameters and values from 'model' make it in.
257   * @example
258   * dst = {a: 1, b: 2, c: "three"};
259   * src = {c: "three", d: "four"};
260   * merged = help.mergeWithModel(dst,src);
261   * merged; // => {a: 1, b: 2, c: 3, d: "four"};
262   */	
263 	mergeWithModel:function(data,model) {
264 		if (data==null) data={};
265 		if (model!=null)
266 			for (var i in model)
267 				if (data[i]==null) data[i]=model[i];
268 		return data;
269 	},
270 	
271   /**
272   * Merges two sets of parameters together overwriting any existing parameters. This merges model->data, and if data and model share parameters, data's are overwritten by model's.
273   * @param {Object} data An object containing a set of parameters, the destination of the merge.
274   * @param {Object} model An object containing a set of parameters, the source of the merge.
275   * @returns A merged model where the values of 'model' take precedence over those of 'data'. The 'data' object is returned and will be an exact copy of 'model', plus any parameters that 'data' had before the merge that 'model' did not.
276   * @example
277   * dst = {a: 1, b: 2, c: "three"};
278   * src = {c: "three", d: "four"};
279   * merged = help.mergeWithModel(dst,src);
280   * merged; // => {a: 1, b: 2, c: "three", d: "four"}
281   */	
282 	copyModel:function(data,model) {
283 		if (data==null) data={};
284 		if (model!=null)
285 			for (var i in model) data[i]=model[i];
286 		return data;
287 	},
288 
289   /**
290   * Creates a subset of an existing set of parameters.
291   * @param {Object} obj An object containing a set of parameters, the source of the data.
292   * @param {Array} attrs An array of strings, containing the names of parameters you wish to copy.
293   * @returns A new set of parameters based on the subset specified.
294   * @example
295   * data = {a: 1, b: 2, c: "three"};
296   * newdata = help.createModel(data, ["a", "c"]);
297   * newdata; // => {a: 1, c: "three"}
298   */	
299 	createModel:function(obj,attrs) {
300 		var ret={};
301 		for (var i=0;i<attrs.length;i++) ret[attrs[i]]=obj[attrs[i]];
302 		return ret;
303 	},
304 	
305   /**
306   * Creates a duplicate of an existing set of parameters.
307   * @param {Object} model An object containing a set of parameters.
308   * @returns A new object, equivalent to 'model'.
309   * @example
310   * data = {a: 1, b: 2, c: "three"};
311   * newdata = help.cloneObject(data);
312   * newdata; // => {a: 1, b: 2, c: "three"}
313   */	
314 	cloneObject:function(model) {
315 		if (!model) return model;
316 		var data={};
317 		for (var i in model) data[i]=model[i];
318 		return data;
319 	},
320 	
321   /**
322   * Sets a tile in the map and draws it. Does not return anything.
323   * @param {Object} ctx The canvas context for the map. Accessed via gbox.getCanvasContext("canvasname")
324   * @param {Object} map The game map object.
325   * @param {Integer} x The index of the tile column within the map array -- so a 1 would mean the second column of tiles. 
326   * @param {Integer} y The index of the tile row within the map array -- so a 1 would mean the second row of tiles. 
327   * @param {Integer} tile The integer representing the new tile you wish to draw. This is its index within the tileset; a null value will erase whatever tile is present.
328   * @param {String} The ID of the map. Defaults to 'map'.
329   * @example
330   * // Remove the second tile to the right and down from the upper left corner of the tile map. Assumes our map canvas is called 'map_canvas'.
331   * help.setTileInMap(gbox.getCanvasContext("map_canvas"),map,1,1,null,"map");
332   */	  
333 	setTileInMap:function(ctx,tilemap,x,y,tile,map) {
334 		var ts=gbox.getTiles(tilemap.tileset);
335 		tilemap[(map==null?"map":map)][y][x]=tile;
336 		if (tile==null)
337 			gbox.blitClear(ctx,{x:x*ts.tilew,y:y*ts.tilew,h:ts.tileh,w:ts.tilew});
338 		else
339 			gbox.blitTile(ctx,{tileset:tilemap.tileset,tile:tile,dx:x*ts.tilew,dy:y*ts.tilew});
340 	},
341 	
342 
343   /**
344   * Returns the Nth element in an array. If the array is shorter than N, it returns the last element of the array.
345   * @param {Array} a An array.
346   * @param {Integer} id An index to the array.
347   * @returns If id > a.length, it returns a[a.length-1]. Otherwise returns a[id].
348   */	
349 	getArrayCapped:function(a,id) {
350 		if (id>=a.length) return a[a.length-1]; else return a[id];
351 	},
352 	
353 	// Get an item of an array of object, using a field as index. is returned the first entry if the field is not valued.
354 	getArrayIndexed:function(a,value,field) {
355 		if (a[0][field]==null) return a[0];
356 		var i=0;
357 		while ((value>a[i][field])&&(i!=a.length-1)) i++;
358 		return a[i];
359 	},
360 	
361 			
362   /**
363   * Converts a quantity of frames into a timestamp formatted "mm:ss:cs" (minutes, seconds, centiseconds). Calculated using the current frames per second.
364   * @param {Integer} frames A quantity of frames.
365   * @returns A string containing a timestamp formatted "mm:ss:cs", representing the length of time it would take to render that many frames.
366   * @example
367   * // Assuming 25 frames per second, Akihabara's default.
368   * timestamp = help.framestotime(25);
369   * timestamp; // => '00:01:00';
370   * timestamp = help.framestotime(25 * 60);
371   * timestamp; // => '01:00:00';  
372   */	
373 	framestotime:function(frames) {
374 		var csec=Math.ceil(frames/gbox.getFps()*100);
375 		return this.prepad((Math.floor(csec/6000)%60),2,"0")+":"+this.prepad((Math.floor(csec/100)%60),2,"0")+":"+this.prepad(csec%100,2,"0");
376 		
377 	},
378 	
379   /**
380   * Reads the value of a query parameter from the URL of the web page. 
381   * @param {String} name The name of the URL parameter.
382   * @returns The value of the URL parameter, as a string.
383   * @example
384   * // If the URL is http://example.com/game.html?lives=3
385   * player.lives = help.geturlparameter("lives");
386   * player.lives; // => 3
387   */	
388 	geturlparameter:function( name ) {
389 	  name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
390 	  var regexS = "[\\?&]"+name+"=([^&#]*)";
391 	  var regex = new RegExp( regexS );
392 	  var results = regex.exec( window.location.href );
393 	  if( results == null )
394 		return "";
395 	  else
396 		return results[1];
397 	},
398 	
399   /**
400   * Writes the contents of an object to a string. Useful for debugging.
401   * @param {Object} Any object.
402   * @returns A string containing all the contents of an object. If the object contains functions, the string will contain the code for those functions.
403   */	
404 	objToStr:function(o) {
405 		var ret="";
406 		for (var n in o) ret+=n+":["+o[n]+"] ";
407 		return ret;
408 	},
409 	
410   /**
411   * Tests whether an object contains a given parameter.
412   * @param {Object} A reference to a parameter of an object.
413   * @returns True if the object contains that parameter, false if it does not.
414   * @example
415   * foo = {a: 1, b: 2};
416   * help.isDefined(foo.a); // => true
417   * help.isDefined(foo.c); // => false
418   */	
419 	isDefined:function(v) {
420 		return ((typeof(v) !== 'undefined') || (v===null));
421 	},
422 	
423   /**
424   * Automatically configures a bunch of settings depending on the web browser and device that is viewing the game. Mostly sets the maximum number of audio channels and touch settings.
425   */	
426 	getDeviceConfig:function() {
427 
428 		var cap;
429 		if (navigator.userAgent.match(/iPhone/i)||navigator.userAgent.match(/iPod/i) || navigator.userAgent.match(/Android/i))
430 			cap={touch:true,width:320};			
431 		else if (navigator.userAgent.match(/iPad/i))
432 			cap={touch:true,width:768,forcedidle:10}; // Forced idle time is needed for correct framerate calculation.
433 		else
434 			cap={zoom:2};
435 		
436 		cap.canaudio=!!(document.createElement('audio').canPlayType);
437 
438 		if (cap.canaudio) {
439 			if (navigator.userAgent.match(/iPad/i)||navigator.userAgent.match(/iPhone/i)||navigator.userAgent.match(/iPod/i)) {
440 				cap.audiocompatmode=2; // Single audio per time, so compatibility mode is needed. Plays only the "bgmusic" channel.
441 				cap.audioteam=1; // Only a member is required in the audioteam.
442 				cap.audioisexperimental=true; // Audio is experimental, since limited.
443 			} else if (navigator.userAgent.match(/Chrome/i)) {
444 				cap.audioteam=3; // Quite low performance on playback responsiveness.
445 			} else if (navigator.userAgent.match(/Firefox/i)) {
446 				cap.audioteam=1; // Testing smaller audioteam
447 				cap.audiopositiondelay=0.3; // Ogg playback is slower 0.3 between MP3 playback. Don't know why :)
448 				cap.audiocreatemode=1; // Firefox is stalling while downloading lot of things
449 			} else if (navigator.userAgent.match(/Minefield/i)) {
450 				cap.audioteam=1; // Testing smaller audioteam
451 				cap.audiocreatemode=1; // Firefox is stalling while downloading lot of things
452 				// Minefield has fixed the 0.3 delay!
453 			} else if (navigator.userAgent.match(/Safari/i)) {
454 				cap.audioteam=1; // Testing smaller audioteam						
455 			} else if (navigator.userAgent.match(/Opera/i)) {
456 				cap.audioteam=1; // Testing smaller audioteam			
457 				cap.audiocreatemode=1; // Do not like audio object cloning very much
458 			} else
459 				cap.audioisexperimental=true; // Audio is just experimental on all other devices.
460 				
461 		}
462 
463 		return cap;
464 	},
465 
466   /**
467   * This provides a number of configurations: fps, display zoom, dynamic frameskip, force touch parameters, etc. Many of these settings can
468   * be set manually by passing an object with the parameters defined, or via URL parameters.
469   * @param {Object} data An optional object containing parameters you wish to set. Works for data.zoom, data.splash, data.width, data.height, data.title, data.fps, and data.padmode.
470   */	
471 	akihabaraInit:function(data) {
472 		if ((typeof data).toLowerCase() == "string") data={title:data};
473 		var device=this.getDeviceConfig();
474 		var footnotes=["MADE WITH AKIHABARA (C)2010 - GPL2/MIT","Project: www.kesiev.com/akihabara","Sources: github.com/kesiev/akihabara"];
475 		document.title=(data.title?data.title:"Akihabara");
476 		if (data.splash) {
477 			if (data.splash.footnotes) 
478 				for (var i=0;i<footnotes.length;i++) data.splash.footnotes.push(footnotes[i]);
479 			gbox.setSplashSettings(data.splash);
480 		}
481 		var screenwidth=(data.width?data.width:(data.portrait?240:320));
482 		var screenheight=(data.height?data.height:(data.portrait?320:240));
483 		if (!data.splash||(data.splash.minilogo==null)) gbox.setSplashSettings({minilogo:"logo"});
484 		if (!data.splash||(data.splash.background==null)) gbox.setSplashSettings({background:"akihabara/splash.png"});
485 		if (!data.splash||(data.splash.minimalTime==null)) gbox.setSplashSettings({minimalTime:3000});
486 		if (!data.splash||(data.splash.footnotes==null)) gbox.setSplashSettings({footnotes:footnotes});
487 		document.body.style.backgroundColor="#000000";
488 		gbox.setScreenBorder(false);
489 		if (help.geturlparameter("statusbar")) gbox.setStatusBar(1);
490 		if (help.geturlparameter("db")) gbox.setDoubleBuffering(true);
491 		if (help.geturlparameter("noautoskip")) gbox.setAutoskip(null);
492 		if (help.geturlparameter("zoom")) gbox.setZoom(help.geturlparameter("zoom")); else
493 	     	if (help.isDefined(data.zoom)) gbox.setZoom(data.zoom); else
494 			if (help.isDefined(device.zoom)) gbox.setZoom(device.zoom); else
495 			if (help.isDefined(device.width)) gbox.setZoom(device.width/screenwidth);
496 		if (help.geturlparameter("fps")) gbox.setFps(help.geturlparameter("fps")*1);
497 			else gbox.setFps((data.fps?data.fps:25));
498 		if (help.geturlparameter("fskip")) gbox.setFrameskip(help.geturlparameter("fskip"));
499 		if (help.geturlparameter("forcedidle")) gbox.setForcedIdle(help.geturlparameter("forcedidle")*1);
500 			else if (help.isDefined(device.forcedidle)) gbox.setForcedIdle(device.forcedidle);
501 		if (help.geturlparameter("canlog")) gbox.setCanLog(true);
502 
503 		gbox.initScreen(screenwidth,screenheight);
504 
505 		if (help.geturlparameter("showplayers")) gbox.setShowPlayers(help.geturlparameter("showplayers")=="yes");
506 		if (help.geturlparameter("canaudio")) gbox.setCanAudio(help.geturlparameter("canaudio")=="yes"); else
507 			gbox.setCanAudio(device.canaudio&&(!device.audioisexperimental||gbox.getFlag("experimental")));
508 		if (help.geturlparameter("audiocompatmode")) gbox.setAudioCompatMode(help.geturlparameter("audiocompatmode")*1); else
509 			if (help.isDefined(device.audiocompatmode)) gbox.setAudioCompatMode(device.audiocompatmode);
510 		if (help.geturlparameter("audioteam")) gbox.setAudioTeam(help.geturlparameter("audioteam")*1); else
511 			if (help.isDefined(device.audioteam)) gbox.setAudioTeam(device.audioteam);
512 		if (help.geturlparameter("loweraudioteam")) gbox.setLowerAudioTeam(help.geturlparameter("loweraudioteam")*1); else
513 			if (help.isDefined(device.loweraudioteam)) gbox.setLowerAudioTeam(device.loweraudioteam);			
514 		if (help.geturlparameter("audiocreatemode")) gbox.setAudioCreateMode(help.geturlparameter("audiocreatemode")*1); else
515 			if (help.isDefined(device.audiocreatemode)) gbox.setAudioCreateMode(device.audiocreatemode);
516 		if (help.geturlparameter("audiodequeuetime")) gbox.setAudioDequeueTime(help.geturlparameter("audiodequeuetime")*1); else
517 			if (help.isDefined(device.audiodequeuetime)) gbox.setAudioDequeueTime(device.audiodequeuetime);
518 		if (help.geturlparameter("audiopositiondelay")) gbox.setAudioPositionDelay(help.geturlparameter("audiopositiondelay")*1); else
519 			if (help.isDefined(device.audiopositiondelay)) gbox.setAudioPositionDelay(device.audiopositiondelay);
520 			
521 			
522 			
523 		if (help.geturlparameter("touch")=="no");
524 			else if ((help.geturlparameter("touch")=="yes")||device.touch)
525 				switch (data.padmode) {
526 					case "fretboard": {
527 						iphofretboard.initialize({h:100,bg:"akihabara/fretboard.png"});		
528 						break;
529 					}
530 					case "none": {
531 						break;
532 					}
533 					default: {
534 						iphopad.initialize({h:100,dpad:"akihabara/dpad.png",buttons:"akihabara/buttons.png",bg:"akihabara/padbg.png"});		
535 						break;
536 					}
537 				}
538 	}
539 }
540