UNPKG

189 kBHTMLView Raw
1<!DOCTYPE html>
2
3<html lang="en">
4<head>
5 <meta charset="utf-8">
6 <meta name="viewport" content="width=device-width">
7 <title>CrossBrowdy API documentation Source: CrossBase/audiovisual/audio/CB_AudioFileCache.js</title>
8
9 <!--[if lt IE 9]>
10 <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
11 <![endif]-->
12 <link type="text/css" rel="stylesheet" href="styles/sunlight.default.css">
13
14 <link type="text/css" rel="stylesheet" href="styles/site.cosmo.css">
15
16</head>
17
18<body style="min-width:800px; overflow-wrap:break-word; word-wrap:break-word; word-break:break-word; line-break:strict; hyphens:none; -webkit-hyphens:none; -moz-hyphens:none;">
19
20<div class="navbar navbar-default navbar-fixed-top ">
21<div class="container">
22 <div class="navbar-header">
23 <a class="navbar-brand" href="index.html">CrossBrowdy API documentation</a>
24 <button class="navbar-toggle" type="button" data-toggle="collapse" data-target="#topNavigation">
25 <span class="icon-bar"></span>
26 <span class="icon-bar"></span>
27 <span class="icon-bar"></span>
28 </button>
29 </div>
30 <div class="navbar-collapse collapse" id="topNavigation">
31 <ul class="nav navbar-nav">
32
33 <li class="dropdown">
34 <a href="namespaces.list.html" class="dropdown-toggle" data-toggle="dropdown">Namespaces<b class="caret"></b></a>
35 <ul class="dropdown-menu inline">
36 <li><a href="CB_Arrays.html">CB_Arrays</a></li><li><a href="CB_AudioDetector.html">CB_AudioDetector</a></li><li><a href="CB_baseSymbols.html">CB_baseSymbols</a></li><li><a href="CB_Client.html">CB_Client</a></li><li><a href="CB_Collisions.html">CB_Collisions</a></li><li><a href="CB_Configuration.html">CB_Configuration</a></li><li><a href="CB_Configuration.CrossBase.html">CB_Configuration.CrossBase</a></li><li><a href="CB_Configuration.CrossBrowdy.html">CB_Configuration.CrossBrowdy</a></li><li><a href="CB_Controllers.html">CB_Controllers</a></li><li><a href="CB_Controllers_Proprietary.html">CB_Controllers_Proprietary</a></li><li><a href="CB_Controllers_Proprietary.WII.html">CB_Controllers_Proprietary.WII</a></li><li><a href="CB_Controllers_Proprietary.WII_U.html">CB_Controllers_Proprietary.WII_U</a></li><li><a href="CB_Device.html">CB_Device</a></li><li><a href="CB_Device.AmbientLight.html">CB_Device.AmbientLight</a></li><li><a href="CB_Device.Battery.html">CB_Device.Battery</a></li><li><a href="CB_Device.Location.html">CB_Device.Location</a></li><li><a href="CB_Device.Motion.html">CB_Device.Motion</a></li><li><a href="CB_Device.Orientation.html">CB_Device.Orientation</a></li><li><a href="CB_Device.Proximity.html">CB_Device.Proximity</a></li><li><a href="CB_Device.Vibration.html">CB_Device.Vibration</a></li><li><a href="CB_Elements.html">CB_Elements</a></li><li><a href="CB_Events.html">CB_Events</a></li><li><a href="CB_Keyboard.html">CB_Keyboard</a></li><li><a href="CB_Keyboard.chars.html">CB_Keyboard.chars</a></li><li><a href="CB_Keyboard.extended.html">CB_Keyboard.extended</a></li><li><a href="CB_Keyboard.keys.html">CB_Keyboard.keys</a></li><li><a href="CB_Modules.html">CB_Modules</a></li><li><a href="CB_Mouse.html">CB_Mouse</a></li><li><a href="CB_Mouse.CursorImage.html">CB_Mouse.CursorImage</a></li><li><a href="CB_Net.html">CB_Net</a></li><li><a href="CB_Net.Fetch.html">CB_Net.Fetch</a></li><li><a href="CB_Net.REST.html">CB_Net.REST</a></li><li><a href="CB_Net.Sockets.html">CB_Net.Sockets</a></li><li><a href="CB_Net.Sockets.SockJS.html">CB_Net.Sockets.SockJS</a></li><li><a href="CB_Net.XHR.html">CB_Net.XHR</a></li><li><a href="CB_Pointer.html">CB_Pointer</a></li><li><a href="CB_Screen.html">CB_Screen</a></li><li><a href="CB_Speaker.html">CB_Speaker</a></li><li><a href="CB_Touch.html">CB_Touch</a></li>
37 </ul>
38 </li>
39
40 <li class="dropdown">
41 <a href="classes.list.html" class="dropdown-toggle" data-toggle="dropdown">Classes<b class="caret"></b></a>
42 <ul class="dropdown-menu inline">
43 <li><a href="CB_AudioFile.html">CB_AudioFile</a></li><li><a href="CB_AudioFile_API.AAPI.html">CB_AudioFile_API.AAPI</a></li><li><a href="CB_AudioFile_API.ACMP.html">CB_AudioFile_API.ACMP</a></li><li><a href="CB_AudioFile_API.SM2.html">CB_AudioFile_API.SM2</a></li><li><a href="CB_AudioFile_API.WAAPI.html">CB_AudioFile_API.WAAPI</a></li><li><a href="CB_AudioFileCache.html">CB_AudioFileCache</a></li><li><a href="CB_AudioFileSprites.html">CB_AudioFileSprites</a></li><li><a href="CB_AudioFileSpritesPool.html">CB_AudioFileSpritesPool</a></li><li><a href="CB_Canvas.html">CB_Canvas</a></li><li><a href="CB_GraphicSprites.html">CB_GraphicSprites</a></li><li><a href="CB_GraphicSpritesScene.html">CB_GraphicSpritesScene</a></li>
44 </ul>
45 </li>
46
47 <li class="dropdown">
48 <a href="global.html" class="dropdown-toggle" data-toggle="dropdown">Global<b class="caret"></b></a>
49 <ul class="dropdown-menu inline">
50 <li><a href="global.html#CB_addCredits">CB_addCredits</a></li><li><a href="global.html#CB_BASE_NAME">CB_BASE_NAME</a></li><li><a href="global.html#CB_baseToBase">CB_baseToBase</a></li><li><a href="global.html#CB_baseToInt">CB_baseToInt</a></li><li><a href="global.html#CB_br2nl">CB_br2nl</a></li><li><a href="global.html#CB_brToNl">CB_brToNl</a></li><li><a href="global.html#CB_combineArraysOrObjects">CB_combineArraysOrObjects</a></li><li><a href="global.html#CB_combineAutomatically">CB_combineAutomatically</a></li><li><a href="global.html#CB_combineJSON">CB_combineJSON</a></li><li><a href="global.html#CB_combineURIParameters">CB_combineURIParameters</a></li><li><a href="global.html#CB_combineURLParameters">CB_combineURLParameters</a></li><li><a href="global.html#CB_console">CB_console</a></li><li><a href="global.html#CB_copyObject">CB_copyObject</a></li><li><a href="global.html#CB_countDecimalDigits">CB_countDecimalDigits</a></li><li><a href="global.html#CB_countDecimalPart">CB_countDecimalPart</a></li><li><a href="global.html#CB_countDecimals">CB_countDecimals</a></li><li><a href="global.html#CB_countIntegerDigits">CB_countIntegerDigits</a></li><li><a href="global.html#CB_countIntegerPart">CB_countIntegerPart</a></li><li><a href="global.html#CB_credits">CB_credits</a></li><li><a href="global.html#CB_CREDITS_DEFAULT">CB_CREDITS_DEFAULT</a></li><li><a href="global.html#CB_forceString">CB_forceString</a></li><li><a href="global.html#CB_forEach">CB_forEach</a></li><li><a href="global.html#CB_getBase64StringObject">CB_getBase64StringObject</a></li><li><a href="global.html#CB_getCookie">CB_getCookie</a></li><li><a href="global.html#CB_getDatum">CB_getDatum</a></li><li><a href="global.html#CB_getJSONPropertyValue">CB_getJSONPropertyValue</a></li><li><a href="global.html#CB_getLZStringObject">CB_getLZStringObject</a></li><li><a href="global.html#CB_getValueIndex">CB_getValueIndex</a></li><li><a href="global.html#CB_getValuePath">CB_getValuePath</a></li><li><a href="global.html#CB_includeJSFile">CB_includeJSFile</a></li><li><a href="global.html#CB_indexOf">CB_indexOf</a></li><li><a href="global.html#CB_init">CB_init</a></li><li><a href="global.html#CB_intToBase">CB_intToBase</a></li><li><a href="global.html#CB_isArray">CB_isArray</a></li><li><a href="global.html#CB_isEmail">CB_isEmail</a></li><li><a href="global.html#CB_isFileLocal">CB_isFileLocal</a></li><li><a href="global.html#CB_isString">CB_isString</a></li><li><a href="global.html#CB_lastIndexOf">CB_lastIndexOf</a></li><li><a href="global.html#CB_ltrim">CB_ltrim</a></li><li><a href="global.html#CB_NAME">CB_NAME</a></li><li><a href="global.html#CB_nl2br">CB_nl2br</a></li><li><a href="global.html#CB_nlToBr">CB_nlToBr</a></li><li><a href="global.html#CB_numberFormat">CB_numberFormat</a></li><li><a href="global.html#CB_numberOfDecimalDigits">CB_numberOfDecimalDigits</a></li><li><a href="global.html#CB_numberOfDecimals">CB_numberOfDecimals</a></li><li><a href="global.html#CB_numberOfIntegerDigits">CB_numberOfIntegerDigits</a></li><li><a href="global.html#CB_OPTIONS">CB_OPTIONS</a></li><li><a href="global.html#CB_parseJSON">CB_parseJSON</a></li><li><a href="global.html#CB_parseString">CB_parseString</a></li><li><a href="global.html#CB_regularExpressionString">CB_regularExpressionString</a></li><li><a href="global.html#CB_renderString">CB_renderString</a></li><li><a href="global.html#CB_replaceAll">CB_replaceAll</a></li><li><a href="global.html#CB_rtrim">CB_rtrim</a></li><li><a href="global.html#CB_scriptPath">CB_scriptPath</a></li><li><a href="global.html#CB_scriptPathCalculate">CB_scriptPathCalculate</a></li><li><a href="global.html#CB_setCookie">CB_setCookie</a></li><li><a href="global.html#CB_setDatum">CB_setDatum</a></li><li><a href="global.html#CB_sizeof">CB_sizeof</a></li><li><a href="global.html#CB_sizeOf">CB_sizeOf</a></li><li><a href="global.html#CB_stringifyJSON">CB_stringifyJSON</a></li><li><a href="global.html#CB_symmetricCall">CB_symmetricCall</a></li><li><a href="global.html#CB_symmetricCallClear">CB_symmetricCallClear</a></li><li><a href="global.html#CB_this">CB_this</a></li><li><a href="global.html#CB_trim">CB_trim</a></li><li><a href="global.html#CB_VERSION">CB_VERSION</a></li>
51 </ul>
52 </li>
53
54 </ul>
55
56 <div class="col-sm-3 col-md-3">
57 <form class="navbar-form" role="search">
58 <div class="input-group">
59 <input type="text" class="form-control" placeholder="Search" name="q" id="search-input">
60 <div class="input-group-btn">
61 <button class="btn btn-default" id="search-submit"><i class="glyphicon glyphicon-search"></i></button>
62 </div>
63 </div>
64 </form>
65 </div>
66
67 </div>
68
69</div>
70</div>
71
72
73<div class="container" id="toc-content" style="width:100%;">
74<div class="row" style="width:100%;">
75
76
77 <div class="col-md-12">
78
79 <div id="main">
80
81
82 <h1 class="page-title">Source: CrossBase/audiovisual/audio/CB_AudioFileCache.js</h1>
83
84<section>
85 <article>
86 <pre
87 class="sunlight-highlight-javascript linenums">/**
88 * @file Audio files cache management. Contains the {@link CB_AudioFileCache} class.
89 * @author Joan Alba Maldonado &lt;workindalian@gmail.com>
90 * @license Creative Commons Attribution 4.0 International. See more at {@link https://crossbrowdy.com/about#what_is_the_crossbrowdy_copyright_and_license}.
91 */
92
93
94/**
95 * Object whose property names are audio formats (they can include just the format as 'audio/ogg' or also the codec as for example 'audio/ogg; codecs="vorbis"') and their value is an array of strings with the URIs (audio file paths or audio data URIs) of the audio files in order of preference. The best audio format for the current client will be tried to be calculated and it will use the first working URI (audio file path or data URI). The more audio formats and URIs provided the better, as it will help to maximize the compatibility with as many clients as possible (as some audio APIs and client just support some formats, or use absolute paths instead of relative ones, etc.). Even with different formats, all provided URIs should belong to the same audio (this means same sound or same music, with same length, etc.). NOTE: Only some clients with some audio APIs will support data URIs.
96 * @example
97 * {
98 * "audio/mp4" : [ "first/path/sound.m4a", "alternative/path/sound.m4a", "alternative/path/2/sound.mp4", ... ],
99 * "audio/ogg" : [ "first/path/sound.opus", "alternative/path/sound.ogg", "alternative/path/2/sound.ogg", ... ],
100 * "audio/mpeg" : [ "first/path/sound.mp3", "alternative/path/sound.mp3", "alternative/path/2/sound.mp3", ... ],
101 * "audio/wav" : [ "first/path/sound.wav", "alternative/path/sound.wav", "alternative/path/2/sound.wav", ... ],
102 * ...
103 * }
104 * @memberof CB_AudioFileCache
105 * @typedef {Object} CB_AudioFileCache.URIS_OBJECT
106 * @property {array} filePaths - Being the name of each property the audio format (it can include just the format as 'audio/ogg' or also the codec as for example 'audio/ogg; codecs="vorbis"'), the value will always be a numeric array of strings with the URIs (audio file paths or audio data URIs) of the audio files in order of preference. The best audio format for the current client will be tried to be calculated and it will use the first working URI (audio file path or data URI). The more audio formats and URIs provided the better, as it will help to maximize the compatibility with as many clients as possible (as some audio APIs and client just support some formats, or use absolute paths instead of relative ones, etc.). Even with different formats, all provided URIs should belong to the same audio (this means same sound or same music, with same length, etc.). NOTE: Only some clients with some audio APIs will support data URIs.
107 */
108
109
110/**
111 * Object with the desired data and options for the audio files cache.
112 * @memberof CB_AudioFileCache
113 * @typedef {Object} CB_AudioFileCache.DATA_OBJECT
114 * @property {CB_AudioFileCache.URIS_OBJECT} URIs - Object whose property names audio formats and their value is an array of strings with the URIs (audio file paths or audio data URIs) of the audio files in order of preference. The best audio format for the current client will be tried to be calculated and it will use the first working URI (audio file path or data URI). The more audio formats and URIs provided the better, as it will help to maximize the compatibility with as many clients as possible (as some audio APIs and client just support some formats, or use absolute paths instead of relative ones, etc.). Even with different formats, all provided URIs should belong to the same audio (this means same sound or same music, with same length, etc.). NOTE: Only some clients with some audio APIs will support data URIs. If a valid value is given, this will be added to the {@link CB_AudioFileCache#URIs} property.
115 * @property {string} [id=""] - Desired identifier for the audio files cache. Internal usage only recommended. If a valid value is given, this will be added to the {@link CB_AudioFileCache#id} property.
116 * @property {array} [preferredAPIs={@link CB_Configuration.CrossBase.CB_AudioFileCache_PREFERRED_AUDIO_APIS}] - Array of strings with the preferred audio API or audio APIs, in order of preference. Possible audio APIs are "WAAPI" ([HTML5 Web Audio API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API}), "SM2" ([SoundManager 2]{@link http://schillmania.com/projects/soundmanager2/}), "ACMP" ([Apache Cordova Media Plugin]{@link https://github.com/apache/cordova-plugin-media}) or "AAPI" ([HTML5 Audio API]{@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/audio}). It will try to calculate and use the best one for the current client. If a valid value is given, this will be added to the {@link CB_AudioFileCache#preferredAPIs} property.
117 * @property {array} [preferredFormats={@link CB_Configuration.CrossBase.CB_AudioFileCache_PREFERRED_AUDIO_FORMATS}] - Array of strings with the preferred audio format or audio formats (they can include just the format as 'audio/ogg' or also the codec as for example 'audio/ogg; codecs="vorbis"'), in order of preference. It will try to calculate and use the best one for the current client. If a valid value is given, this will be added to the {@link CB_AudioFileCache#preferredFormats} property.
118 * @property {integer} [minimumAudioFiles={@link CB_AudioFileCache.minimumAudioFiles_DEFAULT}] - Minimum {@link CB_AudioFile} objects to create internally. It must be an integer being 1 the minimum. If a valid value is given, this will be added to the {@link CB_AudioFileCache#minimumAudioFiles} property.
119 * @property {integer|null} [maximumAudioFiles={@link CB_AudioFileCache.maximumAudioFiles_DEFAULT}] - Maximum {@link CB_AudioFile} objects that are allowed to be created internally. If it is set to null, there will not be a maximum (it will be unlimited). If an integer is provided, it must be the same number or greater than the value set in the {@link CB_AudioFileCache#minimumAudioFiles} property (also provided by the "minimumAudioFiles" of this object), allowing 1 minimum. If a valid value is given, this will be added to the {@link CB_AudioFileCache#maximumAudioFiles} property.
120 * @property {integer} [minimumAudioFilesFree=parseInt({@link CB_AudioFileCache#minimumAudioFiles} * 0.25 + 0.5)] - New {@link CB_AudioFile} objects will be created internally when the number of free {@link CB_AudioFile} objects reaches this limit. If provided, it must be an integer being 0 (zero) the minimum. It will end using a 25% of the {@link CB_AudioFileCache#minimumAudioFiles} by default, rounded to ceil, allowing 0 (zero) minimum. If a valid value is given, this will be added to the {@link CB_AudioFileCache#minimumAudioFilesFree} property.
121 * @property {integer} [newAudioFilesWhenNeeded=Math.min(parseInt({@link CB_AudioFileCache#minimumAudioFiles} * 0.1 + 0.5), 1)] - Number of new {@link CB_AudioFile} objects to create internally when the minimum limit of free {@link CB_AudioFile} objects ({@link CB_AudioFileCache#minimumAudioFilesFree}) is reached. If provided, it must be an integer being 0 (zero) the minimum. It will end using a 10% of the {@link CB_AudioFileCache#minimumAudioFiles} by default, rounded to ceil, allowing 1 minimum. If a valid value is given, this will be added to the {@link CB_AudioFileCache#newAudioFilesWhenNeeded} property.
122 * @property {integer} [retries={@link CB_AudioFileCache.retries_DEFAULT}] - Number of retries to try to load a {@link CB_AudioFile} object internally before trying to load the next possible one (if any). It must be an integer being 0 the minimum. If a valid value is given, this will be added to the {@link CB_AudioFileCache#retries} property.
123 * @property {boolean} [checkManually={@link CB_AudioFileCache.checkManually_DEFAULT}] - Tells whether the {@link CB_AudioFile} objects must be checked automatically or not (manually) by default. If a valid value is given, this will be added to the {@link CB_AudioFileCache#checkManually} property.
124 * @property {boolean} [checkManuallyOnNeededCreated={@link CB_AudioFileCache.checkManuallyOnNeededCreated_DEFAULT}] - Tells whether the {@link CB_AudioFile} objects must be checked automatically or not (manually) when creates a new {@link CB_AudioFile} object needed. If a valid value is given, this will be added to the {@link CB_AudioFileCache#checkManuallyOnNeededCreated} property.
125 * @property {boolean} [checkManuallyOnPlayingFailed={@link CB_AudioFileCache.checkManuallyOnPlayingFailed_DEFAULT}] - Tells whether the {@link CB_AudioFile} objects must be checked automatically or not (manually) when playing one has failed and tries to reload it. If a valid value is given, this will be added to the {@link CB_AudioFileCache#checkManuallyOnPlayingFailed} property.
126 * @property {boolean} [checkManuallyOnCheckingFailed={@link CB_AudioFileCache.checkManuallyOnCheckingFailed_DEFAULT}] - Tells whether the {@link CB_AudioFile} objects must be checked automatically or not (manually) when checking one has failed and tries to reload it. If a valid value is given, this will be added to the {@link CB_AudioFileCache#checkManuallyOnCheckingFailed} property.
127 * @property {function} [onLoad] - Desired function to be called once the cache has been loaded. The first and unique parameter will be an integer with the {@link CB_AudioFile} objects that still need to be checked, if any, being "this" the current {@link CB_AudioFileCache} object. If a valid value is given, this will be added to the {@link CB_AudioFileCache#onLoad} property.
128 * @property {function} [onError] - Desired function to be called when any kind of error happens. The first and unique parameter will be a string with the error description (if it could be determined), being "this" the current {@link CB_AudioFileCache} object. If a valid value is given, this will be added to the {@link CB_AudioFileCache#onError} property.
129 * @property {boolean} [disableAutoLoad=false] - If set to true, it will not create automatically the {@link CB_AudioFile} objects by calling the {@link CB_AudioFileCache#createAudioFiles} method internally. Internal usage only recommended.
130 */
131
132
133/**
134 * The constructor is recommended to be called through a user-driven event (as onClick, onTouch, etc.), as some clients may need this at least the first time in order to be able to play the audio.
135 * @class
136 * @classdesc Class to manage a cache with multiple {@link CB_AudioFile} objects (they should be the same sound although they can be in different formats). This is not only useful for performance purposes but also for being able to play the same sound simultaneously and multiple times in different audio APIs and clients.
137 * @param {CB_AudioFileCache.DATA_OBJECT} [dataObject] - Object with the desired data and options for the audio files cache.
138 * @returns {CB_AudioFileCache} Returns a new {@link CB_AudioFileCache} object.
139 * @todo Do not allow to create one object with an "id" which has already been used (unless the value is undefined, null...).
140 * @todo Method getCopy and static method filterProperties (similar to the ones from {@link CB_GraphicSprites} and {@link CB_GraphicSpritesScene}).
141 */
142var CB_AudioFileCache = function(dataObject)
143{
144 //Creates an instance of this object and returns it in the case that it is being called from an unexpected context:
145 if (this === window || !(this instanceof CB_AudioFileCache)) { return new CB_AudioFileCache(dataObject); }
146
147 //Static properties and constants:
148 /**
149 * Keeps the default volume. If the {@link CB_Configuration.CrossBase.CB_AudioFile_AudioFileCache_USE_SPEAKER_VOLUME_AS_DEFAULT} property is true, this will keep the result of calling the {@link CB_Speaker.getVolume} function. Otherwise, it will use the value of the {@link CB_Configuration.CrossBase.CB_Speaker_DEFAULT_VOLUME} variable.
150 * @constant
151 * @type {number}
152 * @default CB_Configuration.CrossBase.CB_AudioFile_AudioFileCache_USE_SPEAKER_VOLUME_AS_DEFAULT ? CB_Speaker.getVolume() : CB_Configuration.CrossBase.CB_Speaker_DEFAULT_VOLUME
153 */
154 CB_AudioFileCache.prototype.DEFAULT_VOLUME = CB_Configuration[CB_BASE_NAME].CB_AudioFile_AudioFileCache_USE_SPEAKER_VOLUME_AS_DEFAULT ? CB_Speaker.getVolume() : CB_Configuration[CB_BASE_NAME].CB_Speaker_DEFAULT_VOLUME;
155
156 //Properties and variables:
157 /**
158 * Tells whether the cache is unloaded ({@link CB_AudioFileCache.UNLOADED}), loading ({@link CB_AudioFileCache.LOADING}), unchecked ({@link CB_AudioFileCache.UNCHECKED}), checking ({@link CB_AudioFileCache.CHECKING}), loaded ({@link CB_AudioFileCache.LOADED}), failed ({@link CB_AudioFileCache.FAILED}) or aborted ({@link CB_AudioFileCache.ABORTED}).
159 * @var CB_AudioFileCache#status
160 * @readonly
161 * @type {integer}
162 * @default {@link CB_AudioFileCache.UNLOADED}
163 */
164 this.status = CB_AudioFileCache.UNLOADED;
165
166 /**
167 * Stores the identifier for the audio files cache.
168 * @var
169 * @readonly
170 * @type {string}
171 * @default
172 */
173 this.id = "";
174
175 /**
176 * Stores an array of strings with the preferred audio API or audio APIs, in order of preference. Possible audio APIs are "WAAPI" ([HTML5 Web Audio API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API}), "SM2" ([SoundManager 2]{@link http://schillmania.com/projects/soundmanager2/}), "ACMP" ([Apache Cordova Media Plugin]{@link https://github.com/apache/cordova-plugin-media}) or "AAPI" ([HTML5 Audio API]{@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/audio}). Internal usage only recommended.
177 * @var
178 * @readonly
179 * @type {array}
180 * @default CB_Configuration.CrossBase.CB_AudioFileCache_PREFERRED_AUDIO_APIS
181 */
182 this.preferredAPIs = CB_Configuration[CB_BASE_NAME].CB_AudioFileCache_PREFERRED_AUDIO_APIS;
183
184 /**
185 * Stores an array of strings with the preferred audio format or audio formats (they can include just the format as 'audio/ogg' or also the codec as for example 'audio/ogg; codecs="vorbis"'), in order of preference. Internal usage only recommended.
186 * @var
187 * @readonly
188 * @type {array}
189 * @default CB_Configuration.CrossBase.CB_AudioFileCache_PREFERRED_AUDIO_FORMATS
190 */
191 this.preferredFormats = CB_Configuration[CB_BASE_NAME].CB_AudioFileCache_PREFERRED_AUDIO_FORMATS;
192
193 /**
194 * Object whose property names audio formats and their value is an array of strings with the URIs (audio file paths or audio data URIs) of the audio files in order of preference. The more audio formats and URIs provided the better, as it will help to maximize the compatibility with as many clients as possible (as some audio APIs and client just support some formats, or use absolute paths instead of relative ones, etc.). Even with different formats, all provided URIs should belong to the same audio (this means same sound or same music, with same length, etc.). NOTE: Only some clients with some audio APIs will support data URIs. Internal usage only recommended.
195 * @var
196 * @readonly
197 * @type {CB_AudioFileCache.URIS_OBJECT}
198 */
199 this.URIs = {};
200
201 /**
202 * Minimum {@link CB_AudioFile} objects to create internally. It must be an integer being 1 the minimum. Internal usage only recommended.
203 * @var
204 * @readonly
205 * @type {integer}
206 * @default CB_AudioFileCache.minimumAudioFiles_DEFAULT
207 */
208 this.minimumAudioFiles = CB_AudioFileCache.minimumAudioFiles_DEFAULT;
209
210 /**
211 * Maximum {@link CB_AudioFile} objects that are to be created internally. If it is set to null, there will not be a maximum (it will be unlimited). If an integer is provided, it must be the same number or greater than the value set in the {@link CB_AudioFileCache#minimumAudioFiles} property, allowing 1 minimum. Internal usage only recommended.
212 * @var
213 * @readonly
214 * @type {integer|null}
215 * @default CB_AudioFileCache.maximumAudioFiles_DEFAULT
216 */
217 this.maximumAudioFiles = CB_AudioFileCache.maximumAudioFiles_DEFAULT;
218
219 /**
220 * New {@link CB_AudioFile} objects will be created internally when the number of free {@link CB_AudioFile} objects reaches this limit. It must be an integer being 0 (zero) the minimum. Internal usage only recommended.
221 * @var
222 * @readonly
223 * @type {integer}
224 * @default parseInt({@link CB_AudioFileCache#minimumAudioFiles} * 0.25 + 0.5)
225 */
226 this.minimumAudioFilesFree = CB_AudioFileCache._minimumAudioFilesFree_FIRST_VALUE;
227
228 /**
229 * Number of new {@link CB_AudioFile} objects to create internally when the minimum limit of free {@link CB_AudioFile} objects ({@link CB_AudioFileCache#minimumAudioFilesFree}) is reached. It must be an integer being 0 (zero) the minimum. Internal usage only recommended.
230 * @var
231 * @readonly
232 * @type {integer}
233 * @default Math.min(parseInt({@link CB_AudioFileCache#minimumAudioFiles} * 0.1 + 0.5), 1)
234 */
235 this.newAudioFilesWhenNeeded = CB_AudioFileCache._newAudioFilesWhenNeeded_FIRST_VALUE;
236
237 /**
238 * Number of retries to try to load a {@link CB_AudioFile} object internally before trying to load the next possible one internally (if any). It must be an integer being 0 the minimum. Internal usage only recommended.
239 * @var
240 * @readonly
241 * @type {integer}
242 * @default CB_AudioFileCache.retries_DEFAULT
243 */
244 this.retries = CB_AudioFileCache.retries_DEFAULT;
245
246 /**
247 * Tells whether the {@link CB_AudioFile} objects must be checked automatically or not (manually). Internal usage only recommended.
248 * @var
249 * @readonly
250 * @type {boolean}
251 * @default CB_AudioFileCache.checkManually_DEFAULT
252 */
253 this.checkManually = CB_AudioFileCache.checkManually_DEFAULT;
254
255 /**
256 * Tells whether the {@link CB_AudioFile} objects must be checked automatically or not (manually) when creates a new {@link CB_AudioFile} object needed. Internal usage only recommended.
257 * @var
258 * @readonly
259 * @type {boolean}
260 * @default CB_AudioFileCache.checkManuallyOnNeededCreated_DEFAULT
261 */
262 this.checkManuallyOnNeededCreated = CB_AudioFileCache.checkManuallyOnNeededCreated_DEFAULT;
263
264 /**
265 * Tells whether the {@link CB_AudioFile} objects must be checked automatically or not (manually) when playing one has failed and tries to reload it. Internal usage only recommended.
266 * @var
267 * @readonly
268 * @type {boolean}
269 * @default CB_AudioFileCache.checkManuallyOnPlayingFailed_DEFAULT
270 */
271 this.checkManuallyOnPlayingFailed = CB_AudioFileCache.checkManuallyOnPlayingFailed_DEFAULT;
272
273 /**
274 * Tells whether the {@link CB_AudioFile} objects must be checked automatically or not (manually) when checking one has failed and tries to reload it. Internal usage only recommended.
275 * @var
276 * @readonly
277 * @type {boolean}
278 * @default CB_AudioFileCache.checkManuallyOnCheckingFailed_DEFAULT
279 */
280 this.checkManuallyOnCheckingFailed = CB_AudioFileCache.checkManuallyOnCheckingFailed_DEFAULT;
281
282 /**
283 * Desired function to be called once the cache has been loaded. The first and unique parameter will be an integer with the {@link CB_AudioFile} objects that still need to be checked, if any, being "this" the current {@link CB_AudioFileCache} object. Internal usage only recommended.
284 * @var
285 * @readonly
286 * @type {function}
287 * @default
288 */
289 this.onLoad = null;
290
291 /**
292 * Desired function to be called when any kind of error happens. The first and unique parameter will be a string with the error description (if it could be determined), being "this" the current {@link CB_AudioFileCache} object. Internal usage only recommended.
293 * @var
294 * @readonly
295 * @type {function}
296 * @default
297 */
298 this.onError = null;
299
300 /**
301 * Numeric array containing all the {@link CB_AudioFile} objects created internally. Internal usage only recommended.
302 * @var
303 * @readonly
304 * @type {array}
305 * @default
306 */
307 this.audioFiles = [];
308
309 /**
310 * Total number of {@link CB_AudioFile} objects created internally (optimization purposes, to avoid using {@link CB_AudioFileCache#audioFiles}.length). Internal usage only recommended.
311 * @var
312 * @readonly
313 * @type {integer}
314 * @default
315 */
316 this.audioFilesCreated = 0;
317
318 /**
319 * Stack that stores the indexes (belonged to the {@link CB_AudioFileCache#audioFiles} array) of the free {@link CB_AudioFile} objects. Internal usage only recommended.
320 * @var
321 * @readonly
322 * @type {array}
323 * @default
324 */
325 this.audioFilesFree = [];
326
327 /**
328 * Pointer for the {@link CB_AudioFileCache#audioFilesFree} stack (for optimization purposes). Internal usage only recommended.
329 * @var
330 * @readonly
331 * @type {integer}
332 * @default
333 */
334 this.audioFilesFreePointer = -1;
335
336 /**
337 * Object with sound instance identifiers (integers created by the {@link CB_AudioFileCache#play} method) which are going to play (this way we can cancel the sound before it starts playing). Each property name is the identifier of the sound instance and the value will be an object with "cancelled" (boolean, to know whether the sound instance was cancelled or not) and "object" (containing the {@link CB_AudioFile} object used) properties. Internal usage only recommended.
338 * @var
339 * @readonly
340 * @type {Object}
341 * @default
342 */
343 this.soundInstancesQueued = {};
344
345 /**
346 * Stores the minimum duration found among all the {@link CB_AudioFile} objects. Internal usage only recommended.
347 * @var
348 * @readonly
349 * @type {number}
350 * @default 0
351 */
352 this.duration = 0;
353
354 /**
355 * Stores the maximum duration found among all the {@link CB_AudioFile} objects. Internal usage only recommended.
356 * @var
357 * @readonly
358 * @type {number}
359 * @default 0
360 */
361 this.durationMaximum = 0;
362
363
364 //Internal properties:
365 this._URIsListLast = undefined;
366 this._lastSuccededIndexes = {}; //Stores last indexes that were used when an object is created successfully, ordered by URIs and APIs (for optimization purposes).
367 this._checkCacheLoadedTimeout = null;
368 this._checkCacheLoadedTimeoutMs = 500;
369 this._onLoadCalled = false; //Tells whether the onLoad has been called already or not.
370 this._existingObjectIds = [];
371 this._clearAudioFilesTimeout = null;
372 this._createNewAudioFilesIfNeededTimeout = null;
373 ///////this._callRecursivelyIfNotTooLateCalled = false;
374 this._checkingPlaying = false;
375 this._settingAPI = false;
376
377
378 //Calls the constructor of the object when creates an instance:
379 return this._init(dataObject);
380}
381
382
383//Static properties and constants:
384/////CB_AudioFileCache.MAX_VOLUME = CB_Configuration[CB_BASE_NAME].CB_AudioFile_AudioFileCache_USE_SPEAKER_VOLUME_AS_MAXIMUM;
385CB_AudioFileCache._soundInstanceIdUnique = 0;
386
387/**
388 * Status value for audio file cache which is unloaded. Can be used to compare the value returned by the {@link CB_AudioFileCache#getStatus} method. Recommended for internal usage only.
389 * @constant
390 * @type {integer}
391 * @default 0
392 */
393CB_AudioFileCache.UNLOADED = 0;
394
395/**
396 * Status value for an audio file cache which is loading. Can be used to compare the value returned by the {@link CB_AudioFileCache#getStatus} method. Recommended for internal usage only.
397 * @constant
398 * @type {integer}
399 * @default
400 */
401CB_AudioFileCache.LOADING = 1;
402
403/**
404 * Status value for an audio file cache which has not been checked yet. Can be used to compare the value returned by the {@link CB_AudioFileCache#getStatus} method. Recommended for internal usage only.
405 * @constant
406 * @type {integer}
407 * @default
408 */
409CB_AudioFileCache.UNCHECKED = 2;
410
411/**
412 * Status value for an audio file cache which is being checked currently. Can be used to compare the value returned by the {@link CB_AudioFileCache#getStatus} method. Recommended for internal usage only.
413 * @constant
414 * @type {integer}
415 * @default
416 */
417CB_AudioFileCache.CHECKING = 3;
418
419/**
420 * Status value for an audio file cache which has been loaded. Can be used to compare the value returned by the {@link CB_AudioFileCache#getStatus} method. Recommended for internal usage only.
421 * @constant
422 * @type {integer}
423 * @default
424 */
425CB_AudioFileCache.LOADED = 4;
426
427/**
428 * Status value for an audio file cache which failed to be loaded or failed for any other reason. Can be used to compare the value returned by the {@link CB_AudioFileCache#getStatus} method. Recommended for internal usage only.
429 * @constant
430 * @type {integer}
431 * @default
432 */
433CB_AudioFileCache.FAILED = 5;
434
435/**
436 * Status value for an audio file cache which has been aborted. This will happen when the audio file cache has been destroyed with the {@link CB_AudioFileCache#destructor} method. Can be used to compare the value returned by the {@link CB_AudioFileCache#getStatus} method. Recommended for internal usage only.
437 * @constant
438 * @type {integer}
439 * @default
440 */
441CB_AudioFileCache.ABORTED = 6;
442
443/**
444 * Default value for the {@link CB_AudioFileCache#minimumAudioFiles} property.
445 * @constant
446 * @type {integer}
447 * @default
448 */
449CB_AudioFileCache.minimumAudioFiles_DEFAULT = 2;
450
451/**
452 * Default value for the {@link CB_AudioFileCache#maximumAudioFiles} property.
453 * @constant
454 * @type {integer|null}
455 * @default null
456 */
457CB_AudioFileCache.maximumAudioFiles_DEFAULT = null;
458
459/**
460 * Default value for the {@link CB_AudioFileCache#retries} property.
461 * @constant
462 * @type {integer}
463 * @default
464 */
465CB_AudioFileCache.retries_DEFAULT = 1;
466
467/**
468 * Default value for the {@link CB_AudioFileCache#checkManually} property.
469 * @constant
470 * @type {boolean}
471 * @default false
472 */
473CB_AudioFileCache.checkManually_DEFAULT = false;
474
475/**
476 * Default value for the {@link CB_AudioFileCache#checkManuallyOnNeededCreated} property.
477 * @constant
478 * @type {boolean}
479 * @default false
480 */
481CB_AudioFileCache.checkManuallyOnNeededCreated_DEFAULT = false;
482
483/**
484 * Default value for the {@link CB_AudioFileCache#checkManuallyOnPlayingFailed} property.
485 * @constant
486 * @type {boolean}
487 * @default false
488 */
489CB_AudioFileCache.checkManuallyOnPlayingFailed_DEFAULT = false;
490
491/**
492 * Default value for the {@link CB_AudioFileCache#checkManuallyOnCheckingFailed} property.
493 * @constant
494 * @type {boolean}
495 * @default false
496 */
497CB_AudioFileCache.checkManuallyOnCheckingFailed_DEFAULT = false;
498
499CB_AudioFileCache._minimumAudioFilesFree_FIRST_VALUE = 1; //First value for the {@link CB_AudioFileCache#minimumAudioFilesFree} property, although it will end using a 25% of the {@link CB_AudioFileCache#minimumAudioFiles} by default, rounded to ceil, allowing 0 (zero) minimum.
500CB_AudioFileCache._newAudioFilesWhenNeeded_FIRST_VALUE = 1; //First value for the {@link CB_AudioFileCache#newAudioFilesWhenNeeded} property, although it will end using a 10% of the {@link CB_AudioFileCache#minimumAudioFiles} by default, rounded to ceil, allowing 1 minimum.
501
502
503//Constructor:
504CB_AudioFileCache.prototype._init = function(dataObject)
505{
506 /*
507 FORMAT:
508 dataObject =
509 {
510 [id : String,]
511 [preferredAPIs : Array&lt;String>,]
512 [preferredFormats : Array&lt;String>,]
513 URIs : Object,
514 [minimumAudioFiles : Integer,]
515 [maximumAudioFiles : Integer,]
516 [minimumAudioFilesFree : Integer,]
517 [newAudioFilesWhenNeeded : Integer,]
518 [retries : Integer,]
519 [checkManually : Boolean,]
520 [checkManuallyOnNeededCreated : Boolean,]
521 [checkManuallyOnPlayingFailed : Boolean,]
522 [checkManuallyOnCheckingFailed : Boolean,]
523 [disableAutoLoad : Boolean,]
524 [onLoad : Function,]
525 [onError : Function]
526 };
527 */
528
529 //Tries to load the data (if any):
530 this.load(dataObject);
531
532 //Returns the object:
533 return this;
534}
535
536
537/**
538 * Destroys the audio file cache object, including all the internal {@link CB_AudioFile} objects, and frees memory. By default, unless the "preventAbortedStatus" is set to true, sets the current status of the audio file cache object as ABORTED ({@link CB_AudioFileCache.ABORTED} value).
539 * @function
540 * @param {boolean} [stopSounds=false] - Used as the "stopSound" parameter when calling internally the {@link CB_AudioFile#destructor} method for all the {@link CB_AudioFile} objects.
541 * @param {boolean} [preventAbortedStatus=false] - If set to true (not recommended), it will not assign the status of "ABORTED" (it will not assign the value of {@link CB_AudioFileCache.ABORTED} to the {@link CB_AudioFileCache#status} property).
542 */
543CB_AudioFileCache.prototype.destructor = function(stopSounds, preventAbortedStatus)
544{
545 clearTimeout(this._checkCacheLoadedTimeout);
546 clearTimeout(this._clearAudioFilesTimeout);
547 clearTimeout(this._createNewAudioFilesIfNeededTimeout);
548
549 this.cancelSoundInstances(true, true);
550
551 //Destroys all sounds:
552 this.destroyAll(stopSounds);
553
554 //Resets properties to their default value:
555 this.preferredAPIs = CB_Configuration[CB_BASE_NAME].CB_AudioFileCache_PREFERRED_AUDIO_APIS;
556 this.preferredFormats = CB_Configuration[CB_BASE_NAME].CB_AudioFileCache_PREFERRED_AUDIO_FORMATS;
557 this.URIs = {};
558 this.minimumAudioFiles = CB_AudioFileCache.minimumAudioFiles_DEFAULT;
559 this.maximumAudioFiles = CB_AudioFileCache.maximumAudioFiles_DEFAULT;
560 this.minimumAudioFilesFree = CB_AudioFileCache._minimumAudioFilesFree_FIRST_VALUE;
561 this.newAudioFilesWhenNeeded = CB_AudioFileCache._newAudioFilesWhenNeeded_FIRST_VALUE;
562 this.retries = CB_AudioFileCache.retries_DEFAULT;
563 this.checkManually = CB_AudioFileCache.checkManually_DEFAULT;
564 this.checkManuallyOnNeededCreated = CB_AudioFileCache.checkManuallyOnNeededCreated_DEFAULT;
565 this.checkManuallyOnPlayingFailed = CB_AudioFileCache.checkManuallyOnPlayingFailed_DEFAULT;
566 this.checkManuallyOnCheckingFailed = CB_AudioFileCache.checkManuallyOnCheckingFailed_DEFAULT;
567 this.onLoad = null;
568 this.onError = null;
569 this.audioFiles = [];
570 this.audioFilesCreated = 0;
571 this.soundInstancesQueued = {};
572 this.duration = 0;
573 this.durationMaximum = 0;
574
575 //Resets the audioFilesFree stack and its pointer:
576 this.audioFilesFree = [];
577 this.audioFilesFreePointer = -1;
578
579 //Sets the status as ABORTED:
580 if (!preventAbortedStatus) { this.status = CB_AudioFileCache.ABORTED; }
581}
582
583
584/**
585 * Loads the audio file cache with the desired data given. This method is called by the constructor automatically. Recommended to be called through a user-driven event (as onClick, onTouch, etc.), as some clients may need this at least the first time in order to be able to play the audio.
586 * @function
587 * @param {CB_AudioFileCache.DATA_OBJECT} dataObject - Object with the desired data and options for the audio files cache.
588 * @returns {CB_AudioFileCache|null} If a "dataObject" is given, it returns the current {@link CB_AudioFileCache} object. Otherwise, it returns null.
589 */
590CB_AudioFileCache.prototype.load = function(dataObject)
591{
592 if (typeof(dataObject) === "undefined" || dataObject === null) { return null; }
593
594 this.status = CB_AudioFileCache.LOADING; //The cache is loading.
595
596 //Destroys all previous data (if any):
597 this.destructor(true, true); //Also stops all sounds.
598
599 //Sanitizes the given data:
600 dataObject.id = CB_trim(dataObject.id);
601 dataObject.minimumAudioFiles = parseInt(CB_trim(dataObject.minimumAudioFiles));
602 if (dataObject.maximumAudioFiles !== null) { dataObject.maximumAudioFiles = parseInt(CB_trim(dataObject.maximumAudioFiles)); }
603 dataObject.minimumAudioFilesFree = parseInt(CB_trim(dataObject.minimumAudioFilesFree))
604 dataObject.newAudioFilesWhenNeeded = parseInt(CB_trim(dataObject.newAudioFilesWhenNeeded))
605 dataObject.retries = parseInt(CB_trim(dataObject.retries));
606
607 //Sets the new data:
608 if (dataObject.id !== "") { this.id = dataObject.id; }
609 if (CB_isArray(dataObject.preferredAPIs) &amp;&amp; dataObject.preferredAPIs.length > 0 &amp;&amp; CB_trim(dataObject.preferredAPIs.join("")) !== "") { this.preferredAPIs = dataObject.preferredAPIs; }
610 if (CB_isArray(dataObject.preferredFormats) &amp;&amp; dataObject.preferredFormats.length > 0 &amp;&amp; CB_trim(dataObject.preferredFormats.join("")) !== "") { this.preferredFormats = dataObject.preferredFormats; }
611 if (typeof(dataObject.URIs) !== "undefined") { this.URIs = dataObject.URIs; }
612 if (dataObject.minimumAudioFiles !== "" &amp;&amp; !isNaN(dataObject.minimumAudioFiles) &amp;&amp; dataObject.minimumAudioFiles >= 1)
613 {
614 this.minimumAudioFiles = dataObject.minimumAudioFiles;
615 }
616 if (dataObject.maximumAudioFiles === null || dataObject.maximumAudioFiles !== "" &amp;&amp; !isNaN(dataObject.maximumAudioFiles) &amp;&amp; dataObject.maximumAudioFiles >= this.minimumAudioFiles)
617 {
618 this.maximumAudioFiles = dataObject.maximumAudioFiles;
619 }
620 else { this.maximumAudioFiles = CB_AudioFileCache.maximumAudioFiles_DEFAULT; }
621 if (dataObject.minimumAudioFilesFree !== "" &amp;&amp; !isNaN(dataObject.minimumAudioFilesFree) &amp;&amp; dataObject.minimumAudioFilesFree >= 0)
622 {
623 this.minimumAudioFilesFree = dataObject.minimumAudioFilesFree;
624 }
625 else
626 {
627 //Uses a limit of 25% of the minimum by default:
628 this.minimumAudioFilesFree = parseInt(this.minimumAudioFiles * 0.25 + 0.5); //Ceil round.
629 }
630 if (dataObject.newAudioFilesWhenNeeded !== "" &amp;&amp; !isNaN(dataObject.newAudioFilesWhenNeeded) &amp;&amp; dataObject.newAudioFilesWhenNeeded >= 0)
631 {
632 this.newAudioFilesWhenNeeded = dataObject.newAudioFilesWhenNeeded;
633 }
634 else
635 {
636 //Creates a 10% of the minimum by default:
637 this.newAudioFilesWhenNeeded = parseInt(this.minimumAudioFiles * 0.1 + 0.5); //Ceil round.
638 if (this.newAudioFilesWhenNeeded &lt; 1) { this.newAudioFilesWhenNeeded = 1; }
639 }
640
641 if (dataObject.retries !== "" &amp;&amp; !isNaN(dataObject.retries) &amp;&amp; dataObject.retries >= 0)
642 {
643 this.retries = dataObject.retries;
644 }
645
646 if (typeof(dataObject.checkManually) !== "undefined" &amp;&amp; dataObject.checkManually !== null) { this.checkManually = dataObject.checkManually; }
647 if (typeof(dataObject.checkManuallyOnNeededCreated) !== "undefined" &amp;&amp; dataObject.checkManuallyOnNeededCreated !== null) { this.checkManuallyOnNeededCreated = dataObject.checkManuallyOnNeededCreated; }
648 if (typeof(dataObject.checkManuallyOnPlayingFailed) !== "undefined" &amp;&amp; dataObject.checkManuallyOnPlayingFailed !== null) { this.checkManuallyOnPlayingFailed = dataObject.checkManuallyOnPlayingFailed; }
649 if (typeof(dataObject.checkManuallyOnCheckingFailed) !== "undefined" &amp;&amp; dataObject.checkManuallyOnCheckingFailed !== null) { this.checkManuallyOnCheckingFailed = dataObject.checkManuallyOnCheckingFailed; }
650 if (typeof(dataObject.onLoad) === "function") { this.onLoad = dataObject.onLoad; }
651 if (typeof(dataObject.onError) === "function") { this.onError = dataObject.onError; }
652
653 //If we want, loads the needed objects (if any):
654 var disableAutoLoad = false;
655 if (typeof(dataObject.disableAutoLoad) !== "undefined" &amp;&amp; dataObject.disableAutoLoad !== null) { disableAutoLoad = dataObject.disableAutoLoad; }
656 if (!disableAutoLoad) { this.createAudioFiles(this.minimumAudioFiles); } //Creates the minimum number of objects desired.
657
658 return this;
659}
660
661
662/**
663 * Creates the desired number of internal {@link CB_AudioFile} objects (inside the {@link CB_AudioFileCache#audioFiles} property). This method is already called by the {@link CB_AudioFileCache#load} method automatically (unless the "disableAutoLoad" property has been set to true in the "dataObject" given). Recommended to be called through a user-driven event (as onClick, onTouch, etc.), as some clients may need this at least the first time in order to be able to play the audio.
664 * @function
665 * @param {integer} minimumAudioFiles - Minimum {@link CB_AudioFile} objects to create internally. It must be an integer being 1 the minimum. If a valid value is given, this will be added to the {@link CB_AudioFileCache#minimumAudioFiles} property.
666 * @param {boolean} [setAsLoaded=false] - If the {@link CB_AudioFile} objects already created internally (before calling this method) does not reach the number given in the "minimumAudioFiles", this parameter will be ignored. Otherwise, if set to true, it will set the {@link CB_AudioFileCache.status} property as "LOADED" (the value of the {@link CB_AudioFileCache#LOADED} property) after reaching the desired number. If set to false, the {@link CB_AudioFileCache.status} property will be set as "LOADED" {@link CB_AudioFileCache#LOADED} property) if the {@link CB_AudioFileCache#checkManually} property is set to true or set as "UNCHECKED" if the {@link CB_AudioFileCache#checkManually} property is set to false. Internal usage only recommended.
667 * @returns {integer} Returns the number of {@link CB_AudioFile} objects which are intended to be created (they could fail).
668 */
669CB_AudioFileCache.prototype.createAudioFiles = function(minimumAudioFiles, setAsLoaded)
670{
671 this.status = CB_AudioFileCache.LOADING; //The cache is loading.
672
673 if (typeof(minimumAudioFiles) === "undefined" || minimumAudioFiles === null || isNaN(minimumAudioFiles) || minimumAudioFiles &lt; 1) { minimumAudioFiles = this.minimumAudioFiles; }
674
675 //If there is a maximum of files set:
676 if (typeof(this.maximumAudioFiles) !== "undefined" &amp;&amp; this.maximumAudioFiles !== null &amp;&amp; !isNaN(this.maximumAudioFiles) &amp;&amp; this.maximumAudioFiles >= 1)
677 {
678 //If the minimum of files we want is bigger than the maximum, throws an error and exits:
679 if (minimumAudioFiles > this.maximumAudioFiles)
680 {
681 this.errorFunction("Cannot create " + minimumAudioFiles + " audio files. Maximum is " + this.maximumAudioFiles + ".");
682 return 0;
683 }
684 }
685
686 //Sets as the minimum objects to create the number given:
687 this.minimumAudioFiles = minimumAudioFiles;
688
689 //Clears the array of the AudioFiles:
690 this.clearAudioFiles();
691
692 //Creates the objects if they do not exist already:
693 this.audioFilesCreated = 0;
694 var audioFilesCreated = 0;
695 var audioFilesCreating = 0;
696 var audioFile;
697 var that = this;
698 for (var x = 0; x &lt; minimumAudioFiles; x++)
699 {
700 //If an object is needed:
701 if (typeof(this.audioFiles[x]) === "undefined" || this.audioFiles[x] === null)
702 {
703 //this.audioFiles[x] = this.createAudioFile(); //If loads correctly, it will increase the audioFilesCreated property.
704 //Creates a new object:
705 /////setTimeout //Uses a delay to prevent Firefox error ("Media resource [URI] could not be decoded") since AAPI and SM2 call play() method (and many calls to play() method would fail).
706 /////(
707////////// function()
708 /////{
709 audioFile = that.createAudioFile(null, null, null, null, null, null, true); //If loads correctly, it will increase the audioFilesCreated property.
710 audioFilesCreating++;
711 //////},
712 /////////x * 10 + 1
713 //////);
714 //If no object has been created, throws an error (cache status will be FAILED):
715 //////////if (typeof(audioFile) === "undefined" || audioFile === null)
716 {
717 ////////////this.errorFunction("Tried to create the audio object #" + x + " but is undefined or null.");
718 ////////////return; //Exits the function.
719 }
720 }
721 else { audioFilesCreated++; }
722
723 //If the cache has already failed or is aborted, just exits:
724 if (this.status === CB_AudioFileCache.FAILED || this.status === CB_AudioFileCache.ABORTED) { return audioFilesCreating; }
725 }
726
727 //If the files are already created, the cache has finished loading:
728 //if (audioFilesCreated >= minimumAudioFiles) { this.status = CB_AudioFileCache.LOADED; }
729 if (audioFilesCreated >= minimumAudioFiles) { this.status = this.checkManually &amp;&amp; !setAsLoaded ? CB_AudioFileCache.UNCHECKED : CB_AudioFileCache.LOADED; }
730
731 //Stores the number of files already created:
732 this.audioFilesCreated += audioFilesCreated; //It is an addition because some objects could have been created asynchronously.
733
734 return audioFilesCreating;
735}
736
737
738/**
739 * Creates one internal {@link CB_AudioFile} object (inside the {@link CB_AudioFileCache#audioFiles} property). This method is already called by the {@link CB_AudioFileCache#createAudioFiles} method and other methods automatically. Recommended to be called through a user-driven event (as onClick, onTouch, etc.), as some clients may need this at least the first time in order to be able to play the audio. Internal usage only recommended.
740 * @function
741 * @param {CB_AudioFileCache.URIS_OBJECT} [URIs={@link CB_AudioFileCache#URIs}] - Object whose property names audio formats and their value is an array of strings with the URIs (audio file paths or audio data URIs) of the audio files in order of preference. It will try to calculate and use the best audio format for the current client and use the first working URI (audio file path or data URI). The more audio formats and URIs provided the better, as it will help to maximize the compatibility with as many clients as possible (as some audio APIs and client just support some formats, or use absolute paths instead of relative ones, etc.). Even with different formats, all provided URIs should belong to the same audio (this means same sound or same music, with same length, etc.). NOTE: Only some clients with some audio APIs will support data URIs.
742 * @param {array} [preferredAPIs={@link CB_AudioFileCache#preferredAPIs}] - Array of strings with the preferred audio API or audio APIs, in order of preference. Possible audio APIs are "WAAPI" ([HTML5 Web Audio API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API}), "SM2" ([SoundManager 2]{@link http://schillmania.com/projects/soundmanager2/}), "ACMP" ([Apache Cordova Media Plugin]{@link https://github.com/apache/cordova-plugin-media}) or "AAPI" ([HTML5 Audio API]{@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/audio}). It will try to calculate and use the best one for the current client.
743 * @param {array} [preferredFormats={@link CB_AudioFileCache#preferredFormats}] - Array of strings with the preferred audio format or audio formats (they can include just the format as 'audio/ogg' or also the codec as for example 'audio/ogg; codecs="vorbis"'), in order of preference. It will try to calculate and use the best one for the current client.
744 * @param {CB_AudioFile} [audioObject] - A {@link CB_AudioFile} object that we want to reuse instead of creating a new one (for performance purposes).
745 * @param {function} [callbackOk] - Function with no parameters that will be called once the {@link CB_AudioFile} object is created and loaded successfully (or after it has been checked successfully, depending on the desired option), being "this" the {@link CB_AudioFileCache} object itself.
746 * @param {function} [callbackError] - Function called when any error is produced during creation, loading or checking process, etc. The unique parameter will be a string describing the error (if it was possible to be determined), being "this" the {@link CB_AudioFileCache} object itself.
747 * @param {boolean} [storeURIsList=false] - If set to true, it will store internally the valid supported "URIs" from the given ones (needed by the {@link CB_AudioFileCache#setAudioAPIAll} method, for example). Internal usage only recommended.
748 * @param {boolean} [checkAutomatically=false] - If set to true (not recommended), it will call the {@link CB_AudioFile#checkPlaying} method automatically. Otherwise, it will perform according to the value set at the {@link CB_AudioFileCache#checkManually} property. Internal usage only recommended.
749 * @returns {CB_AudioFile|null} If it fails, it returns null. Otherwise, returns the {@link CB_AudioFile} that has been created or reused.
750 */
751CB_AudioFileCache.prototype.createAudioFile = function(URIs, preferredAPIs, preferredFormats, audioObject, callbackOk, callbackError, storeURIsList, checkAutomatically)
752{
753 //If the cache has already failed or is aborted, just exits:
754 if (this.status === CB_AudioFileCache.FAILED || this.status === CB_AudioFileCache.ABORTED) { return null; }
755
756 this.status = CB_AudioFileCache.LOADING; //The cache is loading.
757
758 //If not given, uses default parameters:
759 if (typeof(URIs) === "undefined" || URIs === null) { URIs = this.URIs; }
760 if (!CB_isArray(preferredAPIs) || preferredAPIs.length === 0 || CB_trim(preferredAPIs.join("")) === "") { preferredAPIs = this.preferredAPIs; }
761 if (!CB_isArray(preferredFormats) || preferredFormats.length === 0 || CB_trim(preferredFormats.join("")) === "") { preferredFormats = this.preferredFormats; }
762
763 //Filters the audio APIs to just use the supported ones:
764 preferredAPIs = CB_AudioDetector.getSupportedAPIs(preferredAPIs);
765 //If preferredAPIs is empty, throws the error and exits:
766 if (preferredAPIs.length === 0) { this.errorFunction("No API supported from the provided ones."); return null; }
767
768 //Filters the audio formats to just use the supported ones (also orders them with the "probably" ones first):
769 var preferredFormatsSupported = CB_AudioDetector.getSupportedAudioFormats(preferredFormats, ["probably", "maybe"]);
770 if (preferredFormatsSupported.length > 0) { preferredFormats = preferredFormatsSupported; } //Only uses the filtered ones if there is at least one.
771 else { this.errorFunction("No format supported from the provided ones."); return null; }
772
773 //Filters the URIs given to just use the ones whose format is supported:
774 var URIsList = [];
775 var preferredFormatsLength = preferredFormats.length;
776 var y, URIsListCurrentLength, isDataURI;
777 for (var x = 0; x &lt; preferredFormatsLength; x++)
778 {
779 //If the support format has URIs associated:
780 if (typeof(URIs[preferredFormats[x]]) !== "undefined" &amp;&amp; CB_isArray(URIs[preferredFormats[x]]))
781 {
782 URIsListCurrentLength = URIs[preferredFormats[x]].length;
783 for (y = 0; y &lt; URIsListCurrentLength; y++)
784 {
785 //Only stores it if this kind of URI (data URI or normal one) is supported by the format:
786 if (!CB_isString(URIs[preferredFormats[x]][y])) { continue; }
787 isDataURI = (URIs[preferredFormats[x]][y].substring(0, 5).toLowerCase() === "data:");
788 if (CB_AudioDetector.isAudioFormatSupported(preferredFormats[x], isDataURI) !== "")
789 {
790 //Stores the current URI:
791 URIsList[URIsList.length] = URIs[preferredFormats[x]][y++];
792 }
793 }
794 }
795 }
796
797 //If there are not URIs supported, throws the error and exits:
798 if (URIsList.length === 0) { this.errorFunction("No URI supported from the provided ones."); return null; }
799 else if (storeURIsList) { this._URIsListLast = URIsList; }
800
801 //Returns the object created:
802 return this._createAudioFileObjectRecursively(URIsList, preferredAPIs, audioObject, callbackOk, callbackError, null, null, null, null, null, checkAutomatically);
803}
804
805
806//Function that creates an audio file object trying given URIs and given APIs (internal usage only):
807CB_AudioFileCache.prototype._createAudioFileObjectRecursively = function(URIsList, preferredAPIs, audioObject, callbackOk, callbackError, URIsListIndex, stopAtURIsListIndex, preferredAPIsIndex, stopAtPreferredAPIsIndex, retryNumber, checkAutomatically)
808{
809 this.status = CB_AudioFileCache.LOADING; //The cache is loading.
810
811 //If there are not URIs supported, throws the error and exits:
812 if (!CB_isArray(URIsList) || URIsList.length === 0) { this.errorFunction("The URIs provided are not in an array or its length is 0."); return null; }
813
814 //If not given, uses default parameters:
815 if (typeof(URIsListIndex) === "undefined" || URIsListIndex === null || isNaN(URIsListIndex)) { URIsListIndex = 0; }
816 if (typeof(preferredAPIsIndex) === "undefined" || preferredAPIsIndex === null || isNaN(preferredAPIsIndex)) { preferredAPIsIndex = 0; }
817 if (typeof(retryNumber) === "undefined" || retryNumber === null || isNaN(retryNumber)) { retryNumber = 0; }
818
819 //If it does not exist yet, creates the last succeeded indexes for the given URIs and the given preferred APIs:
820 if (typeof(this._lastSuccededIndexes[URIsList]) === "undefined" || this._lastSuccededIndexes[URIsList] === null)
821 {
822 this._lastSuccededIndexes[URIsList] = {};
823 }
824 if (typeof(this._lastSuccededIndexes[URIsList][preferredAPIs]) === "undefined" || this._lastSuccededIndexes[URIsList][preferredAPIs] === null)
825 {
826 this._lastSuccededIndexes[URIsList][preferredAPIs] = { "URIsListIndex" : 0, "preferredAPIsIndex" : 0 }; //The first time, starts at the beginning.
827 }
828
829 //If this is not a recursive call (stopAtURIsListIndex will still not be created):
830 if (typeof(stopAtURIsListIndex) === "undefined" || stopAtURIsListIndex === null)
831 {
832 //We continue from the last succeeded API and last succeeded URI (optimization purposes):
833 URIsListIndex = this._lastSuccededIndexes[URIsList][preferredAPIs]["URIsListIndex"];
834 preferredAPIsIndex = this._lastSuccededIndexes[URIsList][preferredAPIs]["preferredAPIsIndex"];
835
836 //Calculates when we should stop trying to create the object (the last API and last URI we should try):
837 stopAtPreferredAPIsIndex = preferredAPIsIndex;
838 stopAtURIsListIndex = URIsListIndex - 1;
839 if (stopAtURIsListIndex &lt; 0)
840 {
841 stopAtURIsListIndex = URIsList.length - 1;
842 stopAtPreferredAPIsIndex--;
843 if (stopAtPreferredAPIsIndex &lt; 0)
844 {
845 stopAtPreferredAPIsIndex = preferredAPIs.length - 1;
846 }
847 }
848 }
849
850 var that = this;
851
852 //Function to call when the object is created successfully:
853 var callbackOkFunction =
854 function()
855 {
856 //Stores the API index and the URI index to use the next time (for optimization purposes):
857 that._lastSuccededIndexes[URIsList][preferredAPIs]["URIsListIndex"] = URIsListIndex;
858 that._lastSuccededIndexes[URIsList][preferredAPIs]["preferredAPIsIndex"] = preferredAPIsIndex;
859
860 if (typeof(callbackOk) === "function") { callbackOk.call(that); }
861 };
862
863
864 //Function to call when the object has failed (has not been created):
865 var callbackErrorFunction =
866 function(error)
867 {
868 //If the cache has already failed or is aborted, just exits:
869 if (that.status === CB_AudioFileCache.FAILED || that.status === CB_AudioFileCache.ABORTED) { return; }
870 ///////if (allAttemptsFailed) { return; }
871
872 //If we have already tried all, throws an error (the status of the cache will be set to FAILED):
873 if (preferredAPIsIndex === stopAtPreferredAPIsIndex &amp;&amp; URIsListIndex === stopAtURIsListIndex &amp;&amp; retryNumber === that.retries)
874 {
875 ///////allAttemptsFailed = true;
876 that.errorFunction("A new audio object could not be created. All attempts failed. Last message: " + error);
877 if (typeof(callbackError) === "function") { callbackError.call(that, "A new audio object could not be created. All attempts failed. Last message: " + error); }
878 return;
879 }
880 //...otherwise, continues trying:
881 else
882 {
883 retryNumber++; //Increases the retries counter.
884
885 //If the current retries are more than the allowed ones, passes to try the next method:
886 if (retryNumber > that.retries)
887 {
888 //Sanitizes the indexes for the next recursive call:
889 URIsListIndex++;
890 URIsListIndex %= URIsList.length;
891 if (URIsListIndex === 0)
892 {
893 preferredAPIsIndex++;
894 preferredAPIsIndex %= preferredAPIs.length;
895 }
896 retryNumber = 0;
897 }
898
899 //Calls the function again:
900 that._createAudioFileObjectRecursively(URIsList, preferredAPIs, audioObject, callbackOk, callbackError, URIsListIndex, stopAtURIsListIndex, preferredAPIsIndex, stopAtPreferredAPIsIndex, retryNumber, checkAutomatically);
901 //that._createAudioFileObject(URIsList[URIsListIndex], null, preferredAPIs[preferredAPIsIndex], callbackOk, callbackError, audioObject);
902 }
903 };
904
905 this._onLoadCalled = false; //Forces to call onLoad function again.
906
907 audioObject = this._createAudioFileObject(URIsList[URIsListIndex], null, preferredAPIs[preferredAPIsIndex], callbackOkFunction, callbackErrorFunction, audioObject, checkAutomatically);
908
909 return audioObject;
910}
911
912
913//Checks and declares whether the cache is completely loaded or not:
914CB_AudioFileCache.prototype._checkCacheLoaded = function(objectsNeedChecking)
915{
916 clearTimeout(this._checkCacheLoadedTimeout);
917
918 //If the method checkPlayingAll is executing, exits:
919 if (this._checkingPlaying || this._settingAPI) { return; }
920
921 //If the cache has failed or has been aborted or it is already loaded, just exits:
922 if (this.status === CB_AudioFileCache.FAILED || this.status === CB_AudioFileCache.ABORTED || this.status === CB_AudioFileCache.LOADED) { return; }
923
924 //Clears the array of the AudioFiles:
925 this.clearAudioFiles(true);
926
927 if (typeof(objectsNeedChecking) === "undefined" || objectsNeedChecking === null || isNaN(objectsNeedChecking) || objectsNeedChecking &lt; 0) { objectsNeedChecking = 0; }
928
929 //If we don't need more objects, the cache is LOADED:
930 if (this.audioFilesCreated >= this.minimumAudioFiles || this.checkManually &amp;&amp; this.audioFilesCreated + objectsNeedChecking >= this.minimumAudioFiles)
931 {
932 //Sets the cache status as loaded:
933 //this.status = CB_AudioFileCache.LOADED;
934 this.status = (objectsNeedChecking > 0) ? CB_AudioFileCache.UNCHECKED : CB_AudioFileCache.LOADED;
935 //If we have not called the onLoad function, calls the onLoad function (if any):
936 if (!this._onLoadCalled &amp;&amp; typeof(this.onLoad) === "function")
937 {
938 this._onLoadCalled = true;
939 this.onLoad.call(this, objectsNeedChecking);
940 }
941 }
942}
943
944
945//Function that creates an audio file (internal usage only):
946CB_AudioFileCache.prototype._createAudioFileObject = function(filePath, audioId, audioAPI, callbackOk, callbackError, audioObject, checkAutomatically)
947{
948 this.status = CB_AudioFileCache.LOADING; //The cache is loading.
949
950 //If there is a limit and we have already reached the maximum of objects allowed, throws an error:
951 if (typeof(this.maximumAudioFiles) !== "undefined" &amp;&amp; this.maximumAudioFiles !== null &amp;&amp; !isNaN(this.maximumAudioFiles) &amp;&amp; this.maximumAudioFiles >= 1)
952 {
953 if (this.audioFilesCreated >= this.maximumAudioFiles)
954 {
955 this.errorFunction("A new object cannot be created. Maximum (" + this.maximumAudioFiles + ") reached.");
956 return null;
957 }
958 }
959
960 var that = this;
961
962 //Defines the function to call when the audio object is created successfully:
963 var callbackOkFunction =
964 function()
965 {
966 //If the sound needs to be checked, checks it and calls the function again if all is fine:
967 if ((!that.checkManually || checkAutomatically) &amp;&amp; audioObject.getStatus() === CB_AudioFile.UNCHECKED)
968 {
969 audioObject.checkPlaying(callbackOkFunction, callbackErrorFunction, false, false, true);
970 return;
971 }
972
973 //Clears the array of the AudioFiles:
974 that.clearAudioFiles();
975
976 var audioFilesIndex = that.audioFiles.length;
977
978 //If the object already exists, gets its index in the array:
979 if (CB_indexOf(that._existingObjectIds, audioObject.id) !== -1)
980 {
981 //Search the object in the array:
982 var audioFilesLength = that.audioFiles.length;
983 var indexFound = false;
984 for (var x = 0; x &lt; audioFilesLength; x++)
985 {
986 //If the object is defined and not null:
987 if (typeof(that.audioFiles[x]) !== "undefined" &amp;&amp; that.audioFiles[x] !== null)
988 {
989 //If the object ID is the same as the current one:
990 if (typeof(that.audioFiles[x].id) !== "undefined" &amp;&amp; that.audioFiles[x].id === audioObject.id)
991 {
992 //Gets its index and exists the bucle:
993 audioFilesIndex = x;
994
995 indexFound = true;
996
997 //Exists the bucle:
998 break;
999 }
1000 }
1001 }
1002
1003 //The object already existed but could not be found (which means it has been deleted by the Error function), so exits:
1004 //if (!indexFound) { return; } //Avoids calling the Ok function if the Error function has already been called.
1005 }
1006
1007 //Stores the object ID to the existing ones:
1008 that._existingObjectIds[that._existingObjectIds.length] = audioObject.id;
1009
1010 //Inserts the object into the array:
1011 that.audioFiles[audioFilesIndex] = audioObject;
1012
1013 //The new object is free so we insert its index in the stack for free elements:
1014 that.audioFilesFree[++that.audioFilesFreePointer] = audioFilesIndex; //Also increases the pointer.
1015
1016 //Increases the counter of the objects created:
1017 that.audioFilesCreated++;
1018
1019 //Clears the array of the AudioFiles:
1020 that.clearAudioFiles();
1021
1022 //If we don't need more objects, we will check again after some time (before declaring the cache as LOADED) because some objects can still fire onerror (specially with SM2):
1023 /*
1024 clearTimeout(that._checkCacheLoadedTimeout); //Stops the previous timeout to check whether the cache is loaded (if any).
1025 if (that.audioFilesCreated >= that.minimumAudioFiles)
1026 {
1027 that._checkCacheLoadedTimeout = setTimeout(function() { that._checkCacheLoaded.call(that); }, that._checkCacheLoadedTimeoutMs);
1028 }
1029 */
1030
1031 //Stores the minimum and maximum duration found:
1032 var duration = audioObject.getDuration();
1033 if (duration > 0 &amp;&amp; (that.duration === 0 || duration &lt; that.duration)) { that.duration = duration; }
1034 if (duration > that.durationMaximum) { that.durationMaximum = duration; }
1035
1036 //Calls the given OK function (if any):
1037 if (typeof(callbackOk) === "function") { callbackOk.call(that); }
1038 };
1039
1040 //Defines the function to call when the audio object could not be created (failed):
1041 var callbackErrorFunction =
1042 function(error)
1043 {
1044 //Clears the array of the AudioFiles:
1045 that.clearAudioFiles();
1046
1047 //If the object was already stored in the array (already existed):
1048 if (CB_indexOf(that._existingObjectIds, audioObject.id) !== -1)
1049 {
1050 //Finds its index in the array:
1051 var audioFilesLength = that.audioFiles.length;
1052 for (var x = 0; x &lt; audioFilesLength; x++)
1053 {
1054 //If the object is defined and not null:
1055 if (typeof(that.audioFiles[x]) !== "undefined" &amp;&amp; that.audioFiles[x] !== null)
1056 {
1057 //If the object ID is the same as the current one:
1058 if (typeof(that.audioFiles[x].id) !== "undefined" &amp;&amp; that.audioFiles[x].id === audioObject.id)
1059 {
1060 //Deletes the object from the array:
1061 that.audioFiles[x] = null;
1062
1063 //This object should not count as created anymore:
1064 that.audioFilesCreated--;
1065
1066 //Clears the array of the AudioFiles:
1067 that.clearAudioFiles();
1068
1069 //Exists the bucle:
1070 break;
1071 }
1072 }
1073 }
1074 }
1075
1076 //Stores the object ID to the existing ones:
1077 that._existingObjectIds[that._existingObjectIds.length] = audioObject.id;
1078
1079 //Calls the given error function (if any):
1080 if (typeof(callbackError) === "function") { callbackError.call(that, error); }
1081 };
1082
1083 //If it does not exist yet, tries to create the object:
1084 if (typeof(audioObject) === "undefined" || audioObject === null)
1085 {
1086 this.DEFAULT_VOLUME = CB_Configuration[CB_BASE_NAME].CB_AudioFile_AudioFileCache_USE_SPEAKER_VOLUME_AS_DEFAULT ? CB_Speaker.getVolume() : CB_Configuration[CB_BASE_NAME].CB_Speaker_DEFAULT_VOLUME;
1087 audioObject =
1088 new CB_AudioFile
1089 (
1090 filePath, //filePath.
1091 null, //audioId.
1092 { autoLoad: true, autoPlay: false, loop: false, volume: CB_Configuration[CB_BASE_NAME].CB_AudioFile_AudioFileCache_MUTE_ON_LOAD_AND_CHECKING ? 0 : this.DEFAULT_VOLUME }, //options (volume is zero to prevent hearing the sound in some web clients).
1093 audioAPI, //audioAPI.
1094 callbackOkFunction, //callbackOk.
1095 callbackErrorFunction //callbackError.
1096 );
1097 }
1098 //...otherwise, if the object already exists, loads it again with the new desired options:
1099 else
1100 {
1101 //audioObject.setVolume(0); //Sets volume to zero to prevent hearing the sound in some web clients.
1102 if (CB_Configuration[CB_BASE_NAME].CB_AudioFile_AudioFileCache_MUTE_ON_LOAD_AND_CHECKING) { audioObject.mute(); } //Sets volume to zero to prevent hearing the sound in some web clients.
1103 audioObject.load
1104 (
1105 filePath, //filePath.
1106 audioAPI, //audioAPI.
1107 false, //autoPlay.
1108 callbackOkFunction, //callbackOk.
1109 callbackErrorFunction, //callbackError.
1110 true //ignoreOldValues.
1111 );
1112 }
1113
1114 return audioObject;
1115}
1116
1117
1118/**
1119 * Cleans the array of the {@link CB_AudioFile} objects (taking off the undefined or null ones) which is in the {@link CB_AudioFileCache#audioFiles} property, just keeping the valid ones and clearing (destroying and removing) the others. For performance purposes. Internal usage only recommended.
1120 * @function
1121 * @param {boolean} [avoidCallingCheckCacheLoaded=false] - If set to false and neither the {@link CB_AudioFileCache#checkPlayingAll} nor the {@link CB_AudioFileCache#setAudioAPIAll} methods are being executed, it will call the {@link CB_AudioFileCache#_checkCacheLoaded} internal method which will call the "onLoad" function defined in the {@link CB_AudioFileCache#onLoad} property if the number of needed {@link CB_AudioFile} objects has been reached (after performing the cleaning process). Internal usage only recommended.
1122 * @returns {array} Returns the value of the {@link CB_AudioFileCache#audioFiles} property.
1123 */
1124CB_AudioFileCache.prototype.clearAudioFiles = function(avoidCallingCheckCacheLoaded)
1125{
1126 clearTimeout(this._clearAudioFilesTimeout);
1127
1128 var audioFilesClean = [];
1129 var audioFilesFree = [];
1130 var audioFilesFreePointer = -1;
1131 var existingIDs = [];
1132 var someChecking = false;
1133 var audioFilesLength = this.audioFiles.length;
1134 var y = 0;
1135 var objectsLoaded = 0;
1136 var objectsNeedChecking = 0;
1137 var duration = 0;
1138 var durationMaximum = 0;
1139 var durationCurrent = 0;
1140 for (var x = 0; x &lt; audioFilesLength; x++)
1141 {
1142 //If the object is defined and not null:
1143 if (typeof(this.audioFiles[x]) !== "undefined" &amp;&amp; this.audioFiles[x] !== null)
1144 {
1145 //If the object status exists (it means it is a real CB_AudioFile object):
1146 if (typeof(this.audioFiles[x].getStatus) !== "undefined")
1147 {
1148 //If the object is LOADED, LOADING or CHECKING or UNCHECKED, we keep it:
1149 if (this.audioFiles[x].getStatus() === CB_AudioFile.LOADED || this.audioFiles[x].getStatus() === CB_AudioFile.LOADING || this.audioFiles[x].getStatus() === CB_AudioFile.CHECKING || this.audioFiles[x].getStatus() === CB_AudioFile.UNCHECKED)
1150 {
1151 //Only adds if it has not been added before:
1152 if (CB_indexOf(existingIDs, this.audioFiles[x].id) === -1)
1153 {
1154 audioFilesClean[y] = this.audioFiles[x];
1155 existingIDs[y++] = this.audioFiles[x].id;
1156
1157 //If the object is still unchecked or checking, we will wait for it:
1158 if (this.audioFiles[x].getStatus() === CB_AudioFile.CHECKING || this.audioFiles[x].getStatus() === CB_AudioFile.UNCHECKED) { objectsNeedChecking++; someChecking = true; }
1159 //...otherwise, if it is LOADED, or its LOADING but its internal object is LOADED (it happens when changing API):
1160 else if (this.audioFiles[x].getStatus() === CB_AudioFile.LOADED || this.audioFiles[x].getStatus() === CB_AudioFile.LOADING &amp;&amp; this.audioFiles[x].getStatus(true) === CB_AudioFile.LOADED)
1161 //|| this.checkManually &amp;&amp; (this.audioFiles[x].getStatus() === CB_AudioFile.UNCHECKED || this.audioFiles[x].getStatus() === CB_AudioFile.CHECKING) //, or we want to check manually and it is UNCHECKED or CHECKING, increases the counter for loaded objects
1162 {
1163 objectsLoaded++;
1164 //If the sound is not playing, it is a free object:
1165 if (!this.audioFiles[x].isPlaying())
1166 {
1167 audioFilesFree[++audioFilesFreePointer] = x; //Stores the position of the object in the array.
1168 }
1169
1170 durationCurrent = this.audioFiles[x].getDuration();
1171 if (durationCurrent > 0 &amp;&amp; (duration === 0 || durationCurrent &lt; duration)) { duration = durationCurrent; }
1172 if (durationCurrent > durationMaximum) { durationMaximum = durationCurrent; }
1173 }
1174 }
1175 }
1176 //...otherwise, just destroy it:
1177 else
1178 {
1179 this.audioFiles[x].destructor(true, false, true); //Also stops it and avoids firing its onStop.
1180 this.audioFiles[x] = null;
1181 }
1182 }
1183 }
1184 }
1185
1186 //Sets the real audio files created:
1187 this.audioFilesCreated = objectsLoaded;
1188
1189 this.audioFiles = audioFilesClean; //Stores the new clean array.
1190
1191 this.audioFilesFree = audioFilesFree; //Stores the free objects encountered.
1192 this.audioFilesFreePointer = audioFilesFreePointer; //Stores the pointer for the array of the free objects.
1193
1194 //Stores the minimum and maximum duration found:
1195 this.duration = duration;
1196 this.durationMaximum = durationMaximum;
1197
1198 var that = this;
1199
1200 if (!avoidCallingCheckCacheLoaded &amp;&amp; !this._checkingPlaying &amp;&amp; !this._settingAPI) //Avoids calling the function if checkPlayingAll or setAudioAPIAll are being executed.
1201 {
1202 clearTimeout(this._checkCacheLoadedTimeout); //Stops the previous timeout to check whether the cache is loaded (if any).
1203 //If minimum objects are loaded or we are using manual checkingn and minimum objects are loaded or unchecked/checking:
1204 if (this.audioFilesCreated >= this.minimumAudioFiles || this.checkManually &amp;&amp; this.audioFilesCreated + objectsNeedChecking >= this.minimumAudioFiles)
1205 {
1206 //Calls the function to check whether the cache is loaded or not:
1207 this._checkCacheLoadedTimeout = setTimeout(function() { that._checkCacheLoaded.call(that, objectsNeedChecking); }, this._checkCacheLoadedTimeoutMs);
1208 }
1209 }
1210
1211 //If some were checking, calls the function again after some time:
1212 if (!this.checkManually &amp;&amp; someChecking)
1213 {
1214 this._clearAudioFilesTimeout =
1215 setTimeout
1216 (
1217 function() { that.clearAudioFiles.call(that); },
1218 1
1219 );
1220 }
1221
1222 return this.audioFiles;
1223}
1224
1225
1226/**
1227 * If found, takes a given {@link CB_AudioFile} object off the {@link CB_AudioFileCache#audioFiles} property (and reloads it if we want to). NOTE: It does neither destroy nor remove the {@link CB_AudioFile} object so it can be used for other purposes (and if a {@link CB_AudioFile} object is given, it will be tried to be reused by the {@link CB_AudioFileCache#createAudioFile} method internally if it is called). Internal usage only recommended.
1228 * @function
1229 * @param {CB_AudioFile|string} audioObjectOrId - The {@link CB_AudioFile} object or a string with its identifier (not case sensitive) that we want to remove from the {@link CB_AudioFileCache#audioFiles} property. If a {@link CB_AudioFile} object is given, its {@link CB_AudioFile#id} property (which should be unique always) must be set as it is used to identify the object. NOTE: It does neither destroy nor remove the {@link CB_AudioFile} object so it can be used for other purposes (and if a {@link CB_AudioFile} object is given, it will be tried to be reused by the {@link CB_AudioFileCache#createAudioFile} method internally if it is called).
1230 * @param {boolean} [reload=false] - If it is set to true, the {@link CB_AudioFileCache#createAudioFile} method will be called automatically at the end of the process. If a {@link CB_AudioFile} object has been given (through the "audioObjectOrId" parameter) or found by its identifier, it will be tried to be reused by the {@link CB_AudioFileCache#createAudioFile} method (as its "audioObject" parameter).
1231 * @param {boolean} [checkManually=false] - Only used when the "reload" parameter is set to true, to calculate the "checkAutomatically" parameter when calling the {@link CB_AudioFileCache#createAudioFile} method internally.
1232 * @returns {boolean|CB_AudioFile|null} Returns null if the given "audioObjectOrId" parameter is not a valid {@link CB_AudioFile} object or its {@link CB_AudioFile#id} property is not set or when the "audioObjectOrId" parameter is an empty string. Returns a {@link CB_AudioFile} object, the given one through the "audioObjectOrId" parameter of the first one removed (it should be the first and unique one removed as the ID must be unique), if the {@link CB_AudioFileCache#createAudioFile} method is called internally (it will reuse this {@link CB_AudioFile} object). Otherwise, returns true if the number of internal {@link CB_AudioFile} objects (inside the {@link CB_AudioFileCache#audioFiles} property) has decreased or false otherwise.
1233 * @todo Think about calling the {@link CB_AudioFileCache#createAudioFile} method internally (when the "reload" parameter is set to true) only when the {@link CB_AudioFile} object has been found and removed from the {@link CB_AudioFileCache#audioFiles} property.
1234 */
1235CB_AudioFileCache.prototype.removeAudioFile = function(audioObjectOrId, reload, checkManually)
1236{
1237 if (typeof(audioObjectOrId) === "undefined" || audioObjectOrId === null) { return null; }
1238 if (!(audioObjectOrId instanceof CB_AudioFile))
1239 {
1240 //If a string or number is given, it will be assumed that it is the "id" desired:
1241 if (CB_isString(audioObjectOrId) || !isNaN(audioObjectOrId)) { audioObjectOrId = { "id" : audioObjectOrId + "" }; }
1242 else { return null; }
1243 }
1244
1245 var id = CB_trim(audioObjectOrId.id).toUpperCase();
1246 if (id === "") { return null; }
1247
1248 //Looks through the array of the objects:
1249 var audioFilesNew = [];
1250 var audioFilesFree = [];
1251 var audioFilesFreePointer = -1;
1252 var objectsLoaded = 0;
1253 var audioFilesLength = this.audioFiles.length;
1254 var duration = 0;
1255 var durationMaximum = 0;
1256 var durationCurrent = 0;
1257 for (var x = 0; x &lt; audioFilesLength; x++)
1258 {
1259 //If the object exists:
1260 if (typeof(this.audioFiles[x]) !== "undefined" &amp;&amp; this.audioFiles[x] !== null)
1261 {
1262 //If the object does not have the desired ID:
1263 if (typeof(this.audioFiles[x].id) !== "undefined")
1264 {
1265 //If it is not the desired id, stores it in the new array:
1266 //if (CB_trim(this.audioFiles[x].id).toUpperCase() !== id)
1267 if (this.audioFiles[x].id !== id)
1268 {
1269 audioFilesNew[audioFilesNew.length] = this.audioFiles[x];
1270
1271 //If it is LOADED or its LOADING but its internal object is LOADED (it happens when changing API):
1272 if (this.audioFiles[x].getStatus() === CB_AudioFile.LOADED || this.audioFiles[x].getStatus() === CB_AudioFile.LOADING &amp;&amp; this.audioFiles[x].getStatus(true) === CB_AudioFile.LOADED)
1273 {
1274 objectsLoaded++; //Increases the counter of files loaded (files successfully created).
1275
1276 //If it is not playing, the sound is free:
1277 if (!this.audioFiles[x].isPlaying())
1278 {
1279 audioFilesFree[++audioFilesFreePointer] = x; //Stores the position of the object in the array.
1280 }
1281 }
1282 }
1283 //...else, if the CB_AudioFile object has been found and the "audioObjectOrId" is not a CB_AudioFile object (as for example if the "audioObjectOrId" parameter was a string with the id instead of a CB_AudioFile object):
1284 else if (!(audioObjectOrId instanceof CB_AudioFile)) { audioObjectOrId = this.audioFiles[x]; } //Stores it to call the "this.createAudioFile" method later (if we want to).
1285 //Stores the minimum and maximum duration found:
1286 durationCurrent = this.audioFiles[x].getDuration();
1287 if (durationCurrent > 0 &amp;&amp; (duration === 0 || durationCurrent &lt; duration)) { duration = durationCurrent; }
1288 if (durationCurrent > durationMaximum) { durationMaximum = durationCurrent; }
1289 }
1290 }
1291 }
1292
1293 //Sets the real audio files created:
1294 this.audioFilesCreated = objectsLoaded;
1295
1296 //Replaces the audio files array with the new one (which should lacks of the id we wanted to remove):
1297 this.audioFiles = audioFilesNew;
1298
1299 //Replaces the array of the free audio files and its pointer:
1300 this.audioFilesFree = audioFilesFree; //Stores the free objects encountered.
1301 this.audioFilesFreePointer = audioFilesFreePointer; //Stores the pointer for the array of the free objects.
1302
1303 //Stores the minimum and maximum duration found:
1304 this.duration = duration;
1305 this.durationMaximum = durationMaximum;
1306
1307 //Returns whether the number of internal CB_AudioFile objects has decreased or not:
1308 var returningValue = (this.audioFiles.length &lt; audioFilesLength);
1309
1310 //If we want to reload the sound, we reload it (and will be returned):
1311 if (reload) { returningValue = this.createAudioFile(null, null, null, audioObjectOrId instanceof CB_AudioFile ? audioObjectOrId : null, null, null, null, !checkManually); } //Uses the same audio object.
1312
1313 return returningValue;
1314}
1315
1316
1317//Removes objects with a given status from the cache until reach a desired number (if able):
1318CB_AudioFileCache.prototype._purgeObjectsStatus = function(desiredNumber, status, includePlaying, stopSounds)
1319{
1320 if (typeof(desiredNumber) === "undefined" || desiredNumber === null || isNaN(desiredNumber) || desiredNumber &lt;= 0) { return 0; }
1321
1322 this.clearAudioFiles(true);
1323
1324 var audioFilesLength = this.audioFiles.length;
1325 var existingObjects = audioFilesLength;
1326
1327 if (existingObjects &lt;= this.minimumAudioFiles || existingObjects &lt;= desiredNumber) { return 0; }
1328
1329 //First, removes not playing ones:
1330 for (var x = 0; x &lt; audioFilesLength &amp;&amp; existingObjects !== desiredNumber; x++)
1331 {
1332 //If the object exists and has the desired status, removes it:
1333 if (typeof(this.audioFiles[x]) !== "undefined" &amp;&amp; this.audioFiles[x] !== null)
1334 {
1335 if (this.audioFiles[x].getStatus() === status)
1336 {
1337 if (!this.audioFiles[x].isPlaying())
1338 {
1339 this.audioFiles[x].destructor(stopSounds, false, true); //Avoids firing onStop.
1340 this.audioFiles[x] = null;
1341 existingObjects--;
1342 }
1343 }
1344 }
1345 }
1346
1347 //Secondly, removes playing ones (if we want to):
1348 if (includePlaying)
1349 {
1350 for (x = 0; x &lt; audioFilesLength &amp;&amp; existingObjects !== desiredNumber; x++)
1351 {
1352 //If the object exists and has the desired status, removes it:
1353 if (typeof(this.audioFiles[x]) !== "undefined" &amp;&amp; this.audioFiles[x] !== null)
1354 {
1355 if (this.audioFiles[x].getStatus() === status)
1356 {
1357 this.audioFiles[x].destructor(stopSounds, false, true); //Avoids firing onStop.
1358 this.audioFiles[x] = null;
1359 existingObjects--;
1360 }
1361 }
1362 }
1363 }
1364
1365 this.clearAudioFiles(true);
1366
1367 return audioFilesLength - existingObjects;
1368}
1369
1370
1371/**
1372 * Tries to purge the audio file cache until it reaches a desired number of {@link CB_AudioFile} objects internally (set in the {@link CB_AudioFileCache#audioFiles} property), by removing and destroying some of the current {@link CB_AudioFile} objects. For performance purposes.
1373 * @function
1374 * @param {integer} desiredNumber - The desired number of internal {@link CB_AudioFile} objects that we want to keep in the {@link CB_AudioFileCache#audioFiles} property. It mus be 1 or greater.
1375 * @param {boolean} [setAsMinimumAudioFiles=false] - If set to true, it will set the value of the "desiredNumber" parameter to the {@link CB_AudioFileCache#minimumAudioFiles} property (only when there is a maximum defined in {@link CB_AudioFileCache#maximumAudioFiles}).
1376 * @param {boolean} [includePlaying=false] - If it is set to true and removing non-playing {@link CB_AudioFile} objects was not enough to reach the desired number (defined in the "desiredNumber" parameter), it will also try to remove objects which are being playing currently.
1377 * @param {boolean} [stopSounds=false] - Used as the "stopSound" parameter when calling the {@link CB_AudioFile#destructor} method of each {@link CB_AudioFile} object removed.
1378 * @param {array} [statuses=Array({@link CB_AudioFile.LOADING}, {@link CB_AudioFile.UNCHECKED}, {@link CB_AudioFile.CHECKING}, {@link CB_AudioFile.LOADED})] - Numeric array containing the statuses of the {@link CB_AudioFile} objects that we want this method to authorize to remove. This means that if the returning value of the {@link CB_AudioFile#getStatus} method of a {@link CB_AudioFile} object is not in this list, it will not be tried to be removed (unless they end removed by the {@link CB_AudioFileCache#clearAudioFiles} method called internally). Have in mind that this method will call the {@link CB_AudioFileCache#clearAudioFiles} method internally, which destroys the {@link CB_AudioFile} objects whose {@link CB_AudioFile#getStatus} method returns {@link CB_AudioFile.ABORTED} and {@link CB_AudioFile.FAILED}, so these two statuses need not be indicated. It will respect the order given. Possible values for this array are: {@link CB_AudioFile.UNLOADED}, {@link CB_AudioFile.LOADING}, {@link CB_AudioFile.UNCHECKED}, {@link CB_AudioFile.CHECKING}, {@link CB_AudioFile.LOADED}, {@link CB_AudioFile.FAILED} and {@link CB_AudioFile.ABORTED}.
1379 * @returns {integer} Returns the number of {@link CB_AudioFile} objects removed.
1380 */
1381CB_AudioFileCache.prototype.purge = function(desiredNumber, setAsMinimumAudioFiles, includePlaying, stopSounds, statuses)
1382{
1383 desiredNumber = parseInt(CB_trim(desiredNumber));
1384
1385 if (desiredNumber === "" || isNaN(desiredNumber)) { return 0; }
1386 desiredNumber = parseInt(desiredNumber);
1387 if (desiredNumber &lt;= 0) { return 0; }
1388
1389 if (setAsMinimumAudioFiles &amp;&amp; (typeof(this.maximumAudioFiles) === "undefined" || this.maximumAudioFiles === null || isNaN(this.maximumAudioFiles) || this.maximumAudioFiles === 0 || desiredNumber &lt;= this.maximumAudioFiles))
1390 {
1391 this.minimumAudioFiles = desiredNumber;
1392 }
1393
1394 var objectsRemoved = 0;
1395
1396 if (typeof(statuses) === "undefined" || statuses === null || !CB_isArray(statuses) || statuses.length &lt;= 0)
1397 {
1398 statuses = [ CB_AudioFile.LOADING, CB_AudioFile.UNCHECKED, CB_AudioFile.CHECKING, CB_AudioFile.LOADED ]; //this._purgeObjectStatus will call clearAudioFiles which destroys ABORTED and FAILED.
1399 }
1400
1401 var statusesLength = statuses.length;
1402 for (var x = 0; x &lt; statusesLength; x++)
1403 {
1404 objectsRemoved += this._purgeObjectsStatus(desiredNumber, statuses[x], includePlaying, stopSounds);
1405 if (this.audioFiles.length &lt;= desiredNumber) { break; }
1406 }
1407
1408 return objectsRemoved;
1409}
1410
1411
1412//Creates a new audio file if neeed:
1413CB_AudioFileCache.prototype._createNewAudioFilesIfNeeded = function()
1414{
1415 //If we don't want to create any new file, just exists:
1416 if (this.newAudioFilesWhenNeeded &lt;= 0) { return; }
1417
1418 //If it is not LOADED, exits:
1419 if (this.status !== CB_AudioFileCache.LOADED) { return; }
1420
1421 //If there is not a minimum limit set, just exits:
1422 if (typeof(this.minimumAudioFilesFree) === "undefined" || this.minimumAudioFilesFree === null || isNaN(this.minimumAudioFilesFree)) { return; }
1423
1424 //If we have reached the minimum, creates new ones:
1425 //if (this.audioFilesFreePointer + 1 &lt;= this.minimumAudioFilesFree) //Pointer starts with 0.
1426 if (this.audioFilesFreePointer &lt; this.minimumAudioFilesFree) //Pointer starts with 0.
1427 {
1428 var filesCreated = 0;
1429 var filesAlreadyCreated = 0;
1430 //Counts LOADED, LOADING, UNCHECKED and CHECKING as already created ones (in order not to create more than needed):
1431 var audioFilesLength = this.audioFiles.length;
1432 for (var x = 0; x &lt; audioFilesLength; x++)
1433 {
1434 //If the object exists:
1435 if (typeof(this.audioFiles[x]) !== "undefined" &amp;&amp; this.audioFiles[x] !== null)
1436 {
1437 //if (this.audioFiles[x].getStatus() === CB_AudioFile.LOADING || this.audioFiles[x].getStatus() === CB_AudioFile.UNCHECKED || this.audioFiles[x].getStatus() === CB_AudioFile.CHECKING)
1438 //If it is still LOADED or LOADING but its internal object is LOADED (would happen when changing API), marks the object as free and increments the pointer:
1439 if (this.audioFiles[x].getStatus() === CB_AudioFile.LOADED || this.audioFiles[x].getStatus() === CB_AudioFile.LOADING &amp;&amp; this.audioFiles[x].getStatus(true) === CB_AudioFile.LOADED)
1440 {
1441 filesAlreadyCreated++;
1442 }
1443 else if (this.audioFiles[x].getStatus() === CB_AudioFile.LOADING || this.audioFiles[x].getStatus() === CB_AudioFile.UNCHECKED || this.audioFiles[x].getStatus() === CB_AudioFile.CHECKING)
1444 {
1445 filesCreated++;
1446 }
1447 }
1448 }
1449
1450 /*
1451 if (this.status !== CB_AudioFileCache.LOADED)
1452 {
1453 if (typeof(this.maximumAudioFiles) === "undefined" || this.maximumAudioFiles === null || this.audioFilesCreated + filesCreated &lt; this.maximumAudioFiles)
1454 {
1455 if (filesCreated &lt; this.newAudioFilesWhenNeeded)
1456 {
1457 var that = this;
1458 setTimeout(function() { that._createNewAudioFilesIfNeeded.call(that); }, 1);
1459 }
1460 }
1461 return;
1462 }
1463 */
1464
1465 //Creates the needed files (if any):
1466 while (typeof(this.maximumAudioFiles) === "undefined" || this.maximumAudioFiles === null || isNaN(this.maximumAudioFiles === null) || this.maximumAudioFiles === 0 || filesAlreadyCreated + filesCreated &lt; this.maximumAudioFiles)
1467 {
1468 //Only creates a new file if there is no maximum or it it has not been reached yet:
1469 if (filesCreated &lt; this.newAudioFilesWhenNeeded)
1470 {
1471 this.createAudioFile(null, null, null, null, null, null, null, !this.checkManuallyOnNeededCreated);
1472 filesCreated++;
1473 } else { break; }
1474 }
1475 }
1476}
1477
1478
1479/**
1480 * Object returned by the {@link CB_AudioFileCache#getFreeAudioFile} method.
1481 * @memberof CB_AudioFileCache
1482 * @typedef {Object} CB_AudioFileCache.getFreeAudioFile_OBJECT
1483 * @property {CB_AudioFile|null} object - Contains the {@link CB_AudioFile} object if found or null otherwise.
1484 * @property {string|integer} index - Contains the position of the {@link CB_AudioFile} object inside the {@link CB_AudioFileCache#audioFiles} property if found or "-1" otherwise.
1485 */
1486
1487/**
1488 * Returns a free {@link CB_AudioFile} object, if any (from the {@link CB_AudioFileCache#audioFilesFree} property). Note that this will call the internal {@link CB_AudioFileCache#_createNewAudioFilesIfNeeded} method that could end creating a new {@link CB_AudioFile} object if needed.
1489 * @function
1490 * @param {boolean} [popIt=false] - If set to true, the {@link CB_AudioFile} object will also be "popped" (removed) from the {@link CB_AudioFileCache#audioFilesFree} property.
1491 * @returns {CB_AudioFileCache.getFreeAudioFile_OBJECT} Returns a {@link CB_AudioFileCache.getFreeAudioFile_OBJECT} object.
1492 */
1493CB_AudioFileCache.prototype.getFreeAudioFile = function(popIt)
1494{
1495 var that = this;
1496
1497 //If there is a free object, just returns it:
1498 if (this.audioFilesFreePointer > -1)
1499 {
1500 //Sets the onStop function (which will mark the sound as free) for the object:
1501 var audioFilesIndex = this.audioFilesFree[this.audioFilesFreePointer];
1502 var audioObject = this.audioFiles[audioFilesIndex];
1503
1504 //If we want, pops the element from the array of the free objects:
1505 if (popIt)
1506 {
1507 //Note: Indeed, setting it to null is not needed because we should only care about the elements before the pointer.
1508 this.audioFilesFree[this.audioFilesFreePointer--] = null; //Also decreases the pointer.
1509 }
1510
1511 //Calls the function which creates new objects if needed:
1512 ////////clearTimeout(this._createNewAudioFilesIfNeededTimeout);
1513 this._createNewAudioFilesIfNeededTimeout = setTimeout(function() { that._createNewAudioFilesIfNeeded.call(that); }, 1);
1514
1515 //Returns the object:
1516 return { "object" : audioObject, "index" : audioFilesIndex };
1517 }
1518
1519 //Calls the function which creates new objects if needed:
1520 ////////clearTimeout(this._createNewAudioFilesIfNeededTimeout);
1521 this._createNewAudioFilesIfNeededTimeout = setTimeout(function() { that._createNewAudioFilesIfNeeded.call(that); }, 1);
1522
1523 //If there is no free object, returns null:
1524 return { "object" : null, "index" : -1 };
1525}
1526
1527
1528/**
1529 * Tells the position of a desired {@link CB_AudioFile} object (by its identifier) in the {@link CB_AudioFileCache#audioFiles} property or -1 otherwise.
1530 * @function
1531 * @param {string} id - The identifier of the {@link CB_AudioFile} object (belongs to its {@link CB_AudioFile#id} property) whose position we want to find. Note that the identifier is not case sensitive and it should be unique for each object.
1532 * @returns {integer} Returns the position of a desired {@link CB_AudioFile} object (by its identifier) in the {@link CB_AudioFileCache#audioFiles} property or -1 otherwise.
1533 */
1534CB_AudioFileCache.prototype.getAudioFilePosition = function(id)
1535{
1536 id = CB_trim(id).toUpperCase(); //id = ("" + id).toUpperCase();
1537 if (id === "") { return -1; }
1538
1539 //Looks through the array of the objects:
1540 var audioFilesLength = this.audioFiles.length;
1541 for (var x = 0; x &lt; audioFilesLength; x++)
1542 {
1543 //If the object exists:
1544 if (typeof(this.audioFiles[x]) !== "undefined" &amp;&amp; this.audioFiles[x] !== null)
1545 {
1546 //If the object has the desired ID:
1547 //if (typeof(this.audioFiles[x].id) !== "undefined" &amp;&amp; CB_trim(this.audioFiles[x].id).toUpperCase() === id)
1548 if (typeof(this.audioFiles[x].id) !== "undefined" &amp;&amp; this.audioFiles[x].id === id)
1549 {
1550 return x; //The "x" is the position. There should be only one with that ID (ID should be unique), so we can exit now.
1551 }
1552 }
1553 }
1554
1555 return -1;
1556}
1557
1558
1559/**
1560 * Tells whether a desired {@link CB_AudioFile} object is free (it is in the {@link CB_AudioFileCache#audioFilesFree} property) or not, by its identifier. A free {@link CB_AudioFile} object is an object which is not being used and it is available to be used.
1561 * @function
1562 * @param {string} id - The identifier of the {@link CB_AudioFile} object (belongs to its {@link CB_AudioFile#id} property) that we want to check. Note that the identifier is not case sensitive and it should be unique for each object.
1563 * @returns {boolean} Returns whether a desired {@link CB_AudioFile} object is free (it is in the {@link CB_AudioFileCache#audioFilesFree} property) or not, by its identifier. A free {@link CB_AudioFile} object is an object which is not being used and it is available to be used.
1564 */
1565CB_AudioFileCache.prototype.isAudioFileFree = function(id)
1566{
1567 var position = this.getAudioFilePosition(id);
1568 if (position === -1) { return false; }
1569 return this.isAudioFileFreeByPosition(position);
1570}
1571
1572
1573/**
1574 * Tells whether a given numeric position of a {@link CB_AudioFile} object in the {@link CB_AudioFileCache#audioFiles} property is stored in the {@link CB_AudioFileCache#audioFilesFree} property or not (this means that the {@link CB_AudioFile} object in that position of the {@link CB_AudioFileCache#audioFiles} property is free). A free {@link CB_AudioFile} object is an object which is not being used and it is available to be used.
1575 * @function
1576 * @param {integer} position - Position of the {@link CB_AudioFile} object in the {@link CB_AudioFileCache#audioFiles} property that we want to check whether it is in the {@link CB_AudioFileCache#audioFilesFree} property or not.
1577 * @returns {boolean} Returns whether the given numeric position of a {@link CB_AudioFile} object in the {@link CB_AudioFileCache#audioFiles} property is stored in the {@link CB_AudioFileCache#audioFilesFree} property or not (this means that the {@link CB_AudioFile} object in that position of the {@link CB_AudioFileCache#audioFiles} property is free). A free {@link CB_AudioFile} object is an object which is not being used and it is available to be used.
1578 */
1579CB_AudioFileCache.prototype.isAudioFileFreeByPosition = function(position)
1580{
1581 //Looks through the array of the free objects:
1582 for (var x = 0; x &lt;= this.audioFilesFreePointer; x++)
1583 {
1584 if (typeof(this.audioFilesFree[x]) !== "undefined" &amp;&amp; this.audioFilesFree[x] === position)
1585 {
1586 return true;
1587 }
1588 }
1589 return false;
1590}
1591
1592
1593//Calls play method play again (in the case it has failed) if the delay is not more than the allowed one:
1594CB_AudioFileCache.prototype._callRecursivelyIfNotTooLate = function(startAt, stopAt, loop, volume, allowedRecursiveDelay, allowedRecursiveDelaySkipping, onPlayStart, onStop, startPlayingTime, startAtOriginal, soundInstanceId, _callRecursivelyIfNotTooLateCalledObject)
1595{
1596 ////////if (this._callRecursivelyIfNotTooLateCalled) { return null; }
1597 ///////this._callRecursivelyIfNotTooLateCalled = true;
1598 if (_callRecursivelyIfNotTooLateCalledObject.called) { return null; }
1599 _callRecursivelyIfNotTooLateCalledObject.called = true;
1600
1601
1602 //Finds out the duration of the track:
1603 var startAtReal = startAtOriginal;
1604 var stopAtReal = stopAt;
1605 if (CB_trim(startAtReal) === "") { startAtReal = 0; }
1606 if (CB_trim(stopAtReal) === "") { stopAtReal = 0; }
1607 startAtReal = parseFloat(startAtReal);
1608 stopAtReal = parseFloat(stopAtReal);
1609 if (startAtReal &lt; 0) { startAtReal = 0; }
1610 if (stopAtReal &lt;= 0 || stopAtReal > this.duration) { stopAtReal = this.duration; }
1611 if (startAtReal > stopAtReal || isNaN(startAtReal)) { startAtReal = stopAtReal; }
1612 var durationReal = stopAtReal - startAtReal;
1613
1614 //If the recursive delay is not null and is a valid number:
1615 if (typeof(allowedRecursiveDelay) === "undefined" || allowedRecursiveDelay === null || isNaN(allowedRecursiveDelay) || allowedRecursiveDelay &lt; 0)
1616 {
1617 allowedRecursiveDelay = CB_Configuration[CB_BASE_NAME].CB_AudioFile_AudioFileCache_ALLOWED_RECURSIVE_DELAY_DEFAULT; //We use default value.
1618 }
1619 if (typeof(allowedRecursiveDelaySkipping) === "undefined" || allowedRecursiveDelaySkipping === null || isNaN(allowedRecursiveDelaySkipping) || allowedRecursiveDelaySkipping &lt; 0)
1620 {
1621 //////allowedRecursiveDelaySkipping = this.duration - startAtOriginal; //We use duration as default value.
1622 allowedRecursiveDelaySkipping = durationReal; //We use duration as default value.
1623 //Apply margins (because some web clients have problems to play files near their end):
1624 //if (allowedRecursiveDelaySkipping > 500) { allowedRecursiveDelaySkipping -= 500; }
1625 //else if (allowedRecursiveDelaySkipping > 100) { allowedRecursiveDelaySkipping -= 100; }
1626 //else if (allowedRecursiveDelaySkipping > 10) { allowedRecursiveDelaySkipping -= 10; }
1627 }
1628
1629 var timeNow = CB_Device.getTiming();
1630
1631 var timeExpired = timeNow - startPlayingTime;
1632
1633 //If it is a loop, it should be played always, so we allow any expired time:
1634 if (loop) { timeExpired %= durationReal; }
1635
1636 var that = this;
1637
1638 //If the time expired is less or equal to the delay allowed:
1639 if (timeExpired &lt;= allowedRecursiveDelay)
1640 {
1641 //Calls the function again:
1642 setTimeout
1643 (
1644 function()
1645 {
1646 that.play.call(that, startAtOriginal, stopAt, loop, volume, allowedRecursiveDelay, allowedRecursiveDelaySkipping, onPlayStart, onStop, startPlayingTime, startAtOriginal, soundInstanceId, _callRecursivelyIfNotTooLateCalledObject);
1647 },
1648 1
1649 );
1650 return true;
1651 }
1652 //...otherwise, if the time expired is less or equal to the allowed recursive delay to play skipping:
1653 else if (timeExpired &lt;= allowedRecursiveDelaySkipping)
1654 {
1655 startAtOriginal = parseFloat(startAtOriginal);
1656 //Calls the function again:
1657 setTimeout
1658 (
1659 function()
1660 {
1661 that.play.call(that, startAtOriginal + timeExpired, stopAt, loop, volume, allowedRecursiveDelay, allowedRecursiveDelaySkipping, onPlayStart, onStop, startPlayingTime, startAtOriginal, soundInstanceId, _callRecursivelyIfNotTooLateCalledObject);
1662 },
1663 1
1664 );
1665 return true;
1666 }
1667
1668 return null;
1669}
1670
1671
1672/**
1673 * Clears the sound instances (created by the {@link CB_AudioFileCache#play} method) which have been cancelled.
1674 * @function
1675 * @param {boolean} [clearWithObjectAssociated=false] - If set to true, it will also clear the sound instances which have a {@link CB_AudioFile} object associated.
1676 * @returns {integer} Returns the number of cleared sound instances.
1677 */
1678CB_AudioFileCache.prototype.clearSoundInstances = function(clearWithObjectAssociated)
1679{
1680 var totalBefore = 0;
1681 var kept = 0;
1682 var soundInstancesQueuedNew = {};
1683 for (var soundInstanceId in this.soundInstancesQueued)
1684 {
1685 totalBefore++;
1686 if (typeof(this.soundInstancesQueued[soundInstanceId]) !== "undefined")
1687 {
1688 //If it has not been cancelled or we do not want to clear with associated object and it has an object associated, we keep it:
1689 if (typeof(this.soundInstancesQueued[soundInstanceId].cancelled) === "undefined" || !this.soundInstancesQueued[soundInstanceId].cancelled || !clearWithObjectAssociated &amp;&amp; typeof(this.soundInstancesQueued[soundInstanceId].object) !== "undefined" &amp;&amp; this.soundInstancesQueued[soundInstanceId].object !== null)
1690 {
1691 kept++;
1692 soundInstancesQueuedNew[soundInstanceId] = this.soundInstancesQueued[soundInstanceId];
1693 }
1694 }
1695 }
1696
1697 this.soundInstancesQueued = soundInstancesQueuedNew;
1698
1699 return totalBefore - kept;
1700}
1701
1702
1703/**
1704 * Cancels (to prevent they start playing) or enables all sound instances (created by the {@link CB_AudioFileCache#play} method).
1705 * @function
1706 * @param {boolean} [cancel=false] - Defines whether we want to cancel them or enable them.
1707 * @param {boolean} [affectWithObjectAssociated=false] - If set to true, it will also affect the sound instances which have a {@link CB_AudioFile} object associated.
1708 * @returns {integer} Returns the number of sound instances modified.
1709 */
1710CB_AudioFileCache.prototype.cancelSoundInstances = function(cancel, affectWithObjectAssociated)
1711{
1712 var performed = 0;
1713 for (var soundInstanceId in this.soundInstancesQueued)
1714 {
1715 if (this.cancelSoundInstance(soundInstanceId, cancel, affectWithObjectAssociated)) { performed++; }
1716 }
1717 return performed;
1718}
1719
1720
1721/**
1722 * Cancels (to prevent it starts playing) or enables a sound instance (created by the {@link CB_AudioFileCache#play} method), by its identifier.
1723 * @function
1724 * @param {integer} soundInstanceId - The identifier (integer) of the sound instance we want to affect.
1725 * @param {boolean} [cancel=false] - Defines whether we want to cancel it or enable it.
1726 * @param {boolean} [affectWithObjectAssociated=false] - If set to true, it will also affect the sound instance even it has a {@link CB_AudioFile} object associated.
1727 * @returns {boolean} Returns true if the sound instance has been modified or false otherwise.
1728 */
1729CB_AudioFileCache.prototype.cancelSoundInstance = function(soundInstanceId, cancel, affectWithObjectAssociated)
1730{
1731 //If the sound instance exists and the setting given is not already being used, sets it and returns true:
1732 cancel = !!cancel;
1733 if (typeof(this.soundInstancesQueued[soundInstanceId]) !== "undefined" &amp;&amp; typeof(this.soundInstancesQueued[soundInstanceId].cancelled) !== "undefined" &amp;&amp; this.soundInstancesQueued[soundInstanceId].cancelled !== cancel)
1734 {
1735 if (affectWithObjectAssociated || typeof(this.soundInstancesQueued[soundInstanceId].object) === "undefined" || this.soundInstancesQueued[soundInstanceId].object === null)
1736 {
1737 this.soundInstancesQueued[soundInstanceId].cancelled = cancel;
1738 return true;
1739 }
1740 }
1741 return false;
1742}
1743
1744
1745/**
1746 * Gets the {@link CB_AudioFile} object associated to a given sound instance ID (created by the {@link CB_AudioFileCache#play} method), if any, or null otherwise.
1747 * @function
1748 * @param {integer} soundInstanceId - The identifier (integer) of the sound instance we want to get.
1749 * @param {boolean} [avoidCancelled=false] - If set to true, it will not return the {@link CB_AudioFile} object if its sound instance has been cancelled.
1750 * @returns {CB_AudioFile|null} Returns the {@link CB_AudioFile} object associated to a given sound instance ID, if any, or null otherwise.
1751 */
1752CB_AudioFileCache.prototype.getAudioFileBySoundInstanceId = function(soundInstanceId, avoidCancelled)
1753{
1754 //If the sound instance ID has an object associated:
1755 if (typeof(this.soundInstancesQueued[soundInstanceId]) !== "undefined" &amp;&amp; typeof(this.soundInstancesQueued[soundInstanceId].object) !== "undefined")
1756 {
1757 if (!avoidCancelled || typeof(this.soundInstancesQueued[soundInstanceId].cancelled) === "undefined" || !this.soundInstancesQueued[soundInstanceId].cancelled)
1758 {
1759 return this.soundInstancesQueued[soundInstanceId].object;
1760 }
1761 }
1762 return null;
1763}
1764
1765
1766//Function to execute when a sound stops:
1767CB_AudioFileCache.prototype._onStopDefaultFunction = function(indexObject, thisObject, statusBefore, soundInstanceId, functionWhenError)
1768{
1769 var wasCancelled = false;
1770 if (typeof(soundInstanceId) !== "undefined" &amp;&amp; soundInstanceId !== null)
1771 {
1772 if (typeof(this.soundInstancesQueued[soundInstanceId]) === "undefined" || this.soundInstancesQueued[soundInstanceId] === null || this.soundInstancesQueued[soundInstanceId].cancelled)
1773 {
1774 wasCancelled = true;
1775 }
1776 else if (typeof(this.soundInstancesQueued[soundInstanceId]) !== "undefined")
1777 {
1778 this.soundInstancesQueued[soundInstanceId].object = null;
1779 this.soundInstancesQueued[soundInstanceId].cancelled = true;
1780 }
1781 }
1782
1783 if (typeof(thisObject) === "undefined" || thisObject === null) { return; }
1784
1785 //thisObject.setVolume(0); //Sets volume to zero (to prevent hearing the sound in some web clients when checkPlaying is called).
1786 if (CB_Configuration[CB_BASE_NAME].CB_AudioFile_AudioFileCache_MUTE_ON_LOAD_AND_CHECKING) { thisObject.mute(); } //Sets volume to zero (to prevent hearing the sound in some web clients when checkPlaying is called).
1787
1788 //If it is still LOADED or LOADING but its internal object is LOADED (would happen when changing API), marks the object as free and increments the pointer:
1789 if (thisObject.getStatus() === CB_AudioFile.LOADED || thisObject.getStatus() === CB_AudioFile.LOADING &amp;&amp; thisObject.getStatus(true) === CB_AudioFile.LOADED)
1790 {
1791 //Marks the object as free and increments the pointer:
1792 this.audioFilesFree[++this.audioFilesFreePointer] = indexObject; //Also increases the pointer ("x" is the position of the object in the array).
1793
1794 //If the status before playing was UNCHECKED (now is LOADED), it has been checked successfully, so it increases the files created counter:
1795 if (statusBefore === CB_AudioFile.UNCHECKED)
1796 {
1797 this.audioFilesCreated++;
1798
1799 //Stores the minimum and maximum duration found:
1800 var duration = thisObject.getDuration();
1801 if (duration > 0 &amp;&amp; (this.duration === 0 || duration &lt; this.duration)) { this.duration = duration; }
1802 if (duration > this.durationMaximum) { this.durationMaximum = duration; }
1803 }
1804 }
1805 //...otherwise, if it is FAILED or ABORTED, removes the sound and reloads another one:
1806 else if (thisObject.getStatus() === CB_AudioFile.FAILED || thisObject.getStatus() === CB_AudioFile.ABORTED || thisObject.getStatus() === CB_AudioFile.UNCHECKED)
1807 {
1808 this.removeAudioFile(thisObject, true, this.checkManuallyOnPlayingFailed);
1809
1810 //Sets the sound instance as not cancelled:
1811 if (!wasCancelled &amp;&amp; typeof(soundInstanceId) !== "undefined" &amp;&amp; soundInstanceId !== null)
1812 {
1813 this.soundInstancesQueued[soundInstanceId] = {};
1814 this.soundInstancesQueued[soundInstanceId].cancelled = false;
1815 this.soundInstancesQueued[soundInstanceId].object = null;
1816 }
1817
1818 if (typeof(functionWhenError) === "function") { functionWhenError(); }
1819 }
1820}
1821
1822
1823/**
1824 * Plays a sound of the cache (if there is any free). If a sound cannot be played, this method can call itself internally again and again (with most of the given parameters being the same, depending on the circumstances) to try to play the sound until a desired time limit is reached. If a {@link CB_AudioFile} object cannot be played and it is determined necessary, it will try to reload it internally (by calling the {@link CB_AudioFileCache#removeAudioFile} method).
1825 * @function
1826 * @param {number} [startAt=0 | {@link CB_AudioFile_API.WAAPI#lastStartAt} | {@link CB_AudioFile_API.SM2#lastStartAt} | {@link CB_AudioFile_API.ACMP#lastStartAt} | {@link CB_AudioFile_API.AAPI#lastStartAt} | stopAt] - Time in milliseconds where we want the audio to start at. If not provided or it is not a valid number, it will use zero (0) as default which belongs to the beginning of the audio. If the value provided is greater than the "stopAt" provided, it will use the value set in the "lastStartAt" property of the used audio API object (which belongs to the "startAt" value the last time that the "play" method was called). If, even using the "lastStartAt" value is still greater than the "stopAt" provided, it will use the same value as the "stopAt" which means it will not play and will stop immediately. Used internally as the "startAt" parameter to call the {@link CB_AudioFile#play} method of the free {@link CB_AudioFile} object (if any).
1827 * @param {number} [stopAt={@link CB_AudioFile_API.WAAPI#getDuration}() | {@link CB_AudioFile_API.SM2#getDuration}() | {@link CB_AudioFile_API.ACMP#getDuration}() | {@link CB_AudioFile_API.AAPI#getDuration}()] - Time in milliseconds where we want the audio to stop at. If not provided or it is not a valid number, it will use the returning value of the "getDuration" method of the used audio API object (which should belong to the total duration of the audio, if it was calculated correctly). Used internally as the "stopAt" parameter to call the {@link CB_AudioFile#play} method of the free {@link CB_AudioFile} object (if any).
1828 * @param {boolean} [loop={@link CB_AudioFile#loop}] - Sets whether we want to play the audio looping (starting again and again) or just play it once. Used internally as the "loop" parameter to call the {@link CB_AudioFile#play} method of the free {@link CB_AudioFile} object (if any).
1829 * @param {number} [volume=CB_Configuration.CrossBase.CB_AudioFile_AudioFileCache_USE_SPEAKER_VOLUME_AS_DEFAULT ? CB_Speaker.getVolume() : CB_Configuration.CrossBase.CB_Speaker_DEFAULT_VOLUME] - Desired volume to play the audio. Used internally as the "volume" parameter to call the {@link CB_AudioFile#setVolume} method of the free {@link CB_AudioFile} object (if any), before playing it.
1830 * @param {boolean} [allowedRecursiveDelay={@link CB_Configuration.CrossBase.CB_AudioFile_AudioFileCache_ALLOWED_RECURSIVE_DELAY_DEFAULT}] - The maximum amount of time (in milliseconds) of delay that we accept before start playing the audio. If the amount of time is overcome, the audio will not play at all. Used only when the audio is not able to play immediately.
1831 * @param {boolean} [allowedRecursiveDelaySkipping=stopAt-startAt] - If provided (uses milliseconds) and the time expired trying to start playing the sound without success is still inside this amount of time provided, it will try to play the sound but skipping the part of the audio which should have already been played already. In other words, it will try to start playing the sound as if the previous non-played part (which should have been playing during the time which already expired) was already being playing silently. Only used when the time set in the "allowedRecursiveDelay" parameter has been reached and the audio did not start playing yet. The default value is the duration of the sound that we want to play (having in mind the real value of the "startAt" and "stopAt" parameters which are calculated internally and can be different from the provided ones in the case that they had any error).
1832 * @param {function} [onPlayStart] - Function to be called when the audio starts playing successfully. The function will be called with the following parameters (in order): "soundInstanceId" (the identifier of the sound instance used), "startAt", "stopAt", "startAtNextLoop", "loop", "avoidDelayedPlay", "allowedRecursiveDelay" and "startPlayingTime", being "this" the {@link CB_AudioFile} object used (if any). Used internally as the "onPlayStart" parameter (wrapped in another function) to call the {@link CB_AudioFile#play} method of the free {@link CB_AudioFile} object (if any).
1833 * @param {function} [onStop] - Function to call when the sound stops playing, with an unique parameter which belongs to the "soundInstanceId" (the identifier of the sound instance used), being "this" the {@link CB_AudioFile} object (if any). Used internally as the "callbackFunction" parameter (wrapped in a function) to call the {@link CB_AudioFile#onStop} method of the free {@link CB_AudioFile} object (if any), before playing it.
1834 * @param {number} [startPlayingTime=CB_Device.getTiming()] - Used internally to calculate the amount of time (in milliseconds) expired without playing the sound. Internal usage only recommended.
1835 * @param {number} [startAtOriginal=startAt] - Used internally to start playing the sound accurately and skipping the part which could not be played before, if the time expired without being played is still inside the amount of time provided in the "allowedRecursiveDelaySkipping" parameter. Internal usage only recommended.
1836 * @param {integer} [soundInstanceId=CB_AudioFileCache._soundInstanceIdUnique++] - The identifier of the sound instance that will be played. Used internally when the function is called recursively in the case that the sound could not be played immediately. Internal usage only recommended.
1837 * @param {Object} [_callRecursivelyIfNotTooLateCalledObject] - Object with just the "called" property (boolean). Used internally to know whether the current execution thread called already the {@link _callRecursivelyIfNotTooLate} internal method for the same sound instance or not. Internal usage only recommended.
1838 * @returns {integer|null} Returns the sound instance ID used if there was one free or null otherwise. To get a sound instance returned does not mean necessarily that the sound started playing so it is necessary to use a callback function as the "onPlayStart" parameter for checking this. The sound instance created (if any), will be cancelled automatically once the sound is stopped.
1839 */
1840CB_AudioFileCache.prototype.play = function(startAt, stopAt, loop, volume, allowedRecursiveDelay, allowedRecursiveDelaySkipping, onPlayStart, onStop, startPlayingTime, startAtOriginal, soundInstanceId, _callRecursivelyIfNotTooLateCalledObject)
1841{
1842 var that = this;
1843
1844 //The first time, we choose a unique sound instance ID:
1845 if (typeof(soundInstanceId) === "undefined" || soundInstanceId === null)
1846 {
1847 soundInstanceId = CB_AudioFileCache._soundInstanceIdUnique++;
1848 this.soundInstancesQueued[soundInstanceId] = {};
1849 this.soundInstancesQueued[soundInstanceId].cancelled = false;
1850 this.soundInstancesQueued[soundInstanceId].object = null;
1851
1852 //Since it is the first time, it modifies the onPlayStart function to assign the audio object to the sound instance ID:
1853 var onPlayStartOld = onPlayStart;
1854 onPlayStart =
1855 function(startAt, stopAt, startAtNextLoop, loop, avoidDelayedPlay, allowedRecursiveDelay, startPlayingTime)
1856 {
1857 that.soundInstancesQueued[soundInstanceId].object = this;
1858 if (typeof(onPlayStartOld) === "function") { onPlayStartOld.call(this, soundInstanceId, startAt, stopAt, startAtNextLoop, loop, avoidDelayedPlay, allowedRecursiveDelay, startPlayingTime); }
1859 };
1860 }
1861
1862 //If the sound instance has been cancelled, we exit:
1863 if (typeof(this.soundInstancesQueued[soundInstanceId]) === "undefined" || this.soundInstancesQueued[soundInstanceId] === null || this.soundInstancesQueued[soundInstanceId].cancelled) { return null; }
1864
1865 ///////this._callRecursivelyIfNotTooLateCalled[soundInstanceId] = false;
1866 if (typeof(_callRecursivelyIfNotTooLateCalledObject) === "undefined" || _callRecursivelyIfNotTooLateCalledObject === null) { _callRecursivelyIfNotTooLateCalledObject = {}; }
1867 _callRecursivelyIfNotTooLateCalledObject.called = false;
1868
1869 //If there is a free audio object, proceeds:
1870 var freeAudioFile = this.getFreeAudioFile(true);
1871 if (typeof(freeAudioFile.object) !== "undefined" &amp;&amp; freeAudioFile.object !== null)
1872 {
1873 var audioObject = freeAudioFile.object;
1874
1875 var statusBefore = audioObject.getStatus();
1876
1877 if (typeof(startPlayingTime) === "undefined" || startPlayingTime === null) { startPlayingTime = CB_Device.getTiming(); }
1878 if (typeof(startAtOriginal) === "undefined" || startAtOriginal === null) { startAtOriginal = startAt; }
1879
1880 //Sets the onStop function that marks the sound as free:
1881 audioObject._fireOnStopByUser = true;
1882 audioObject.onStop
1883 (
1884 function()
1885 {
1886 that._onStopDefaultFunction.call(that, freeAudioFile.index, audioObject, statusBefore, soundInstanceId, function() { that._callRecursivelyIfNotTooLate.call(that, startAt, stopAt, loop, volume, allowedRecursiveDelay, allowedRecursiveDelaySkipping, onPlayStart, onStop, startPlayingTime, startAtOriginal, soundInstanceId, _callRecursivelyIfNotTooLateCalledObject); });
1887 if (typeof(onStop) === "function" &amp;&amp; audioObject._fireOnStopByUser) { onStop.call(audioObject, soundInstanceId); }
1888 },
1889 false //Does not keep any possibly existing onStop function.
1890 );
1891
1892 /////////////////////////audioObject.onStop(onStop, true); //Keeps the previous function (which marks object as free when it stops).
1893
1894 //audioObject.setVolume((statusBefore === CB_AudioFile.UNCHECKED) ? 0 : volume); //If it is UNCHECKED, sets volume to zero (to prevent hearing the sound in some web clients when checkPlaying is called).
1895 audioObject.setVolume(volume);
1896
1897 audioObject.audioFileObject.lastStartAt = startAtOriginal; //Next loop should start from the original startAt.
1898
1899 var played =
1900 audioObject.play
1901 (
1902 startAt, //startAt
1903 stopAt, //stopAt
1904 loop, //loop
1905 true, //avoidDelayedPlay
1906 0, //allowedRecursiveDelay
1907 onPlayStart, //onPlayStart
1908 function()
1909 {
1910 that.soundInstancesQueued[soundInstanceId].object = null;
1911 that.removeAudioFile(audioObject, true, that.checkManuallyOnPlayingFailed);
1912 that._callRecursivelyIfNotTooLate.call(that, startAt, stopAt, loop, volume, allowedRecursiveDelay, allowedRecursiveDelaySkipping, onPlayStart, onStop, startPlayingTime, startAtOriginal, soundInstanceId, _callRecursivelyIfNotTooLateCalledObject);
1913 }, //onLoadError
1914 true //isResume (simulates a resume to force next loop (if any) to start from the original startAt).
1915 );
1916
1917 //If the sound has not been played:
1918 if (played === -1)
1919 {
1920 this.soundInstancesQueued[soundInstanceId].object = null;
1921
1922 //If the sound is FAILED or ABORTED, removes it:
1923 if (audioObject.getStatus() === CB_AudioFile.FAILED || audioObject.getStatus() === CB_AudioFile.ABORTED) { this.removeAudioFile(audioObject, true, this.checkManuallyOnPlayingFailed); }
1924 //If we do not allow delayed play, calls play method again (if the maximum allowed delay has still not expired):
1925 ////////if (avoidDelayedPlay)
1926 ////////{
1927 this._callRecursivelyIfNotTooLate(startAt, stopAt, loop, volume, allowedRecursiveDelay, allowedRecursiveDelaySkipping, onPlayStart, onStop, startPlayingTime, startAtOriginal, soundInstanceId, _callRecursivelyIfNotTooLateCalledObject);
1928 ////////}
1929 }
1930
1931 //////return audioObject;
1932 }
1933 //...otherwise, if there is no object free:
1934 else
1935 {
1936 this.soundInstancesQueued[soundInstanceId].object = null;
1937 //Calls the same function recursively:
1938 if (typeof(startPlayingTime) === "undefined" || startPlayingTime === null) { startPlayingTime = CB_Device.getTiming(); }
1939 if (typeof(startAtOriginal) === "undefined" || startAtOriginal === null) { startAtOriginal = startAt; }
1940 this._callRecursivelyIfNotTooLate(startAt, stopAt, loop, volume, allowedRecursiveDelay, allowedRecursiveDelaySkipping, onPlayStart, onStop, startPlayingTime, startAtOriginal, soundInstanceId, _callRecursivelyIfNotTooLateCalledObject);
1941 //return null;
1942 }
1943 return soundInstanceId;
1944}
1945
1946
1947/**
1948 * Object used by the {@link CB_AudioFileCache#executeFunctionAll} method when the "returnSetTimeoutsArray" parameter is set to true.
1949 * @memberof CB_AudioFileCache
1950 * @typedef {Object} CB_AudioFileCache.executeFunctionAll_OBJECT
1951 * @property {CB_AudioFile} item - The {@link CB_AudioFile} affected.
1952 * @property {integer} setTimeoutReturningValue - The returning value of calling the [setTimeout]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout} internally or null if it was not called, depending on the "delayBetweenEach" parameter.
1953 * @property {number} setTimeoutDelay - The value used as the second parameter when calling the [setTimeout]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout} internally or zero if it was not called, depending on the "delayBetweenEach" parameter.
1954 */
1955
1956/**
1957 * Alias for {@link CB_AudioFileCache#executeFunctionAll}.
1958 * @function CB_AudioFileCache#executeAll
1959 * @see {@link CB_AudioFileCache#executeFunctionAll}
1960 */
1961 /**
1962 * Alias for {@link CB_AudioFileCache#executeFunctionAll}.
1963 * @function CB_AudioFileCache#forEach
1964 * @see {@link CB_AudioFileCache#executeFunctionAll}
1965 */
1966 /**
1967 * Performs a desired action, using the provided function, on all the existing {@link CB_AudioFile} objects or on the desired ones (if provided). Calls the {@link CB_Arrays.executeFunctionAll} function internally and returns its returning value.
1968 * @function
1969 * @param {CB_Arrays.executeFunctionAll_ON_LOOP_CALLBACK} functionEach - Function that will be called for each {@link CB_AudioFile} object. As the first parameter it receives the {@link CB_AudioFile} object of the "audioFiles" being looped, as the second parameter the position of this {@link CB_AudioFile} object in the "audioFiles" array provided (or, if not provided, in the array of the {@link CB_AudioFileCache#audioFiles} property), the third parameter is the array being looped and the fourth parameter will be the "delayBetweenEach" being used, being "this" the {@link CB_AudioFile} object itself.
1970 * @param {number|CB_Arrays.executeFunctionAll_ON_LOOP_CALLBACK} [delayBetweenEach=0] - If a value greater than zero is used, it will be used as the delay desired between each call to the "functionEach" function (calling them using the [setTimeout]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout} function internally). If not provided or the value is 0 (zero) or lower, each call to the "functionEach" function will be performed immediately one after the other. If a function is provided, it will be called with the same parameters as the "functionEach" function and its returning value will be used as the delay (executed every loop for each {@link CB_AudioFile} object).
1971 * @param {array} [audioFiles={@link CB_AudioFileCache#audioFiles}] - A numeric array containing the {@link CB_AudioFile} objects that we want to loop. It should contain only {@link CB_AudioFile} objects which are already in the current audio file cache. If not provided, it will use all the {@link CB_AudioFile} objects contained in the cache.
1972 * @param {boolean} [returnSetTimeoutsArray=false] - Defines whether we want the method to return an integer or a numeric array with information of each [setTimeout]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout} call. Returning an array with information of each [setTimeout]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout} call is only useful when the [setTimeout]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout} function is called internally, which happens when the "delayBetweenEach" parameter is greater than 0 (zero).
1973 * @param {boolean} [delayBetweenEachAffectsFirst=false] - If set to true, the desired delay (if any) will also affect the first call to the "functionEach" function.
1974 * @param {CB_Arrays.executeFunctionAll_ON_FINISH_CALLBACK} [functionFinish] - Function that will be called for when it has finished looping all the items. The first parameter will be the array which was looped, the second parameter will be the number of times that the "functionEach" callback was called (the most likely, matches the number of elements unless they are undefined or null), and the third parameter will be the maximum "delay" used, being "this" the array itself.
1975 * @returns {integer|array} If the "returnSetTimeoutsArray" parameter is set to false, it will return the number of calls to the "functionEach" function that were performed (which should be the same number as the {@link CB_AudioFile} objects given in the "audioFiles" parameter). Otherwise, if the "returnSetTimeoutsArray" is set to true, it will return a numeric array with a {@link CB_AudioFileCache.executeFunctionAll_OBJECT} object for each {@link CB_AudioFile} given. The length of this array will also be the number of calls to the "functionEach" function that were performed. Note that if a value greater than 0 (zero) for the "delayBetweenEach" parameter has been provided, perhaps not all calls of the "functionEach" function will have been performed yet when exiting this method because of the asynchronous nature of the [setTimeout]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout} function.
1976 * @todo Think about only allowing {@link CB_AudioFile} objects (in the "audioFiles" parameter) which are already in the cache (identify them by their ID), to avoid problems.
1977 */
1978CB_AudioFileCache.prototype.executeFunctionAll = CB_AudioFileCache.prototype.executeAll = CB_AudioFileCache.prototype.forEach = function(functionEach, delayBetweenEach, audioFiles, returnSetTimeoutsArray, delayBetweenEachAffectsFirst, functionFinish)
1979{
1980 return CB_Arrays.executeFunctionAll(CB_isArray(audioFiles) ? audioFiles : this.audioFiles, functionEach, delayBetweenEach, returnSetTimeoutsArray, delayBetweenEachAffectsFirst, functionFinish);
1981
1982 /*
1983 if (typeof(functionEach) !== "function") { return returnSetTimeoutsArray ? [] : 0; }
1984 if (typeof(delayBetweenEach) === "undefined" || delayBetweenEach === null || isNaN(delayBetweenEach) || delayBetweenEach &lt; 0) { delayBetweenEach = 0; }
1985
1986 if (typeof(audioFiles) === "undefined" || audioFiles === null || !CB_isArray(audioFiles)) { audioFiles = this.audioFiles; }
1987
1988 var setTimeoutsInformation = [];
1989
1990 var audioFilesLength = audioFiles.length;
1991 var y = 0;
1992 var setTimeoutReturningValue = null;
1993 var setTimeoutDelay = 0;
1994 for (var x = 0; x &lt; audioFilesLength; x++)
1995 {
1996 //If the object exists:
1997 if (typeof(audioFiles[x]) !== "undefined" &amp;&amp; audioFiles[x] !== null)
1998 {
1999 //Executes the given function ("this" parameters will point to the current object):
2000 if (delayBetweenEach === 0)
2001 {
2002 functionEach.call(audioFiles[x], x); //"x" is the position of the object in the array.
2003 }
2004 else
2005 {
2006 new function(x) //Closure to get unique value of "x" variable for each loop.
2007 {
2008 setTimeoutDelay = delayBetweenEachAffectsFirst ? delayBetweenEach * (y + 1) : delayBetweenEach * y;
2009 setTimeoutReturningValue = setTimeout
2010 (
2011 function()
2012 {
2013 functionEach.call(audioFiles[x], x); //"x" is the position of the object in the array.
2014 },
2015 setTimeoutDelay
2016 );
2017 }(x);
2018 }
2019 setTimeoutsInformation[setTimeoutsInformation.length] = { "item" : audioFiles[x], "setTimeoutReturningValue" : setTimeoutReturningValue, "setTimeoutDelay" : setTimeoutDelay };
2020 y++;
2021 }
2022 }
2023
2024 if (returnSetTimeoutsArray) { return setTimeoutsInformation; }
2025 else { return y; }
2026 */
2027}
2028
2029
2030/**
2031 * Destroys all the {@link CB_AudioFile} objects and frees memory, by calling {@link CB_AudioFile#destructor}(stopSounds, false, true).
2032 * @function
2033 * @param {boolean} [stopSounds=false] - Used internally as the "stopSound" parameter when calling the {@link CB_AudioFile#destructor} method of each {@link CB_AudioFile} object.
2034 * @returns {integer} Returns the number of {@link CB_AudioFile} objects whose {@link CB_AudioFile#destructor} has been called.
2035 * @todo Think about implementing an "audioFiles" parameter.
2036 */
2037CB_AudioFileCache.prototype.destroyAll = function(stopSounds)
2038{
2039 //Destroys each object:
2040 var destroyed = this.executeFunctionAll(function() { this.destructor(stopSounds, false, true); }); //Avoids firing onStop.
2041
2042 //No one is free now:
2043 this.audioFilesFree = [];
2044 this.audioFilesFreePointer = -1;
2045
2046 return destroyed;
2047}
2048
2049
2050/**
2051 * Callback function used by the {@link CB_AudioFileCache#checkPlayingAll} method that will be called when all the process was performed successfully.
2052 * @memberof CB_AudioFileCache
2053 * @callback CB_AudioFileCache.checkPlayingAll_CALLBACK_OK
2054 * @param {integer} performedActions - The number of {@link CB_AudioFile} objects that can be played.
2055 * @param {integer} uncheckedObjects - The number of {@link CB_AudioFile} objects that needed to be checked before calling this method.
2056 */
2057
2058/**
2059 * Callback function used by the {@link CB_AudioFileCache#checkPlayingAll} method that will be called when not all was performed successfully.
2060 * @memberof CB_AudioFileCache
2061 * @callback CB_AudioFileCache.checkPlayingAll_CALLBACK_ERROR
2062 * @param {string} errorMessage - A string describing the error, if it could be determined.
2063 * @param {integer} performedActions - The number of {@link CB_AudioFile} objects that can be played.
2064 * @param {integer|undefined} uncheckedObjects - The number of {@link CB_AudioFile} objects that needed to be checked before calling this method (it will be undefined if it could not be determined).
2065 */
2066
2067/**
2068 * Checks whether each {@link CB_AudioFile} object whose {@link CB_AudioFile#getStatus} method returns the "unchecked" value (which belongs to the value of the {@link CB_AudioFile#UNCHECKED} property) can be played or not. After checking, if the audio can be played, the status of the {@link CB_AudioFile} object will get the value of {@link CB_AudioFile.LOADED}. Otherwise, if it cannot be played, the status of the {@link CB_AudioFile} object will get the value of {@link CB_AudioFile.FAILED}. If a {@link CB_AudioFile} object cannot be played and it is determined necessary, it will try to reload it internally (by calling the {@link CB_AudioFileCache#removeAudioFile} method). It will call the {@link CB_AudioFileCache#clearAudioFiles} method internally after finishing. Recommended to be called through a user-driven event (as onClick, onTouch, etc.).
2069 * @function
2070 * @param {CB_AudioFileCache.checkPlayingAll_CALLBACK_OK} [callbackOk] - A function which will be called if all the {@link CB_AudioFile} objects whose {@link CB_AudioFile#getStatus} method returned the "unchecked" value (which belongs to the value of the {@link CB_AudioFile#UNCHECKED} property) could finally be checked successfully and all can be played, being "this" the {@link CB_AudioFileCache} object itself.
2071 * @param {CB_AudioFileCache.checkPlayingAll_CALLBACK_ERROR} [callbackError] - A function which will be called if not all the {@link CB_AudioFile} objects whose {@link CB_AudioFile#getStatus} method returned the "unchecked" value (which belongs to the value of the {@link CB_AudioFile#UNCHECKED} property) could finally be checked successfully and any cannot be played, being "this" the {@link CB_AudioFileCache} object itself. This function will be called immediately if the method was previously called and it is still running currently.
2072 * @param {boolean} [ignoreQueue=false] - Used internally as the "ignoreQueue" parameter when calling the {@link CB_AudioFile#checkPlaying} method of each {@link CB_AudioFile} object.
2073 * @returns {integer} Returns the number of {@link CB_AudioFile} objects whose status belonged to the "unchecked" value (the value of the {@link CB_AudioFile#UNCHECKED} property) before the execution of this method. It will return 0 (zero) if the method is tried to be executed while there is another previous call of it still running. It will also return 0 (zero) if the status of the audio file cache is not loaded (the {@link CB_AudioFileCache#status} property does not belong to the value set in the {@link CB_AudioFileCache.LOADED} property) nor unchecked (the {@link CB_AudioFileCache#status} property does not belong to the value set in the {@link CB_AudioFileCache.UNCHECKED} property).
2074 * @todo Think about implementing an "audioFiles" parameter.
2075 */
2076CB_AudioFileCache.prototype.checkPlayingAll = function(callbackOk, callbackError, ignoreQueue)
2077{
2078 if (this._checkingPlaying)
2079 {
2080 if (typeof(callbackError) === "function")
2081 {
2082 callbackError.call(this, "Method checkPlayingAll cannot be executed again until it finishes.", 0, undefined);
2083 }
2084 return 0;
2085 }
2086
2087 //If the cache status is not LOADED, exits:
2088 if (this.status !== CB_AudioFileCache.LOADED &amp;&amp; this.status !== CB_AudioFileCache.UNCHECKED)
2089 {
2090 if (typeof(callbackError) === "function")
2091 {
2092 callbackError.call(this, "Cache status is not neither loaded nor unchecked.", 0, undefined);
2093 }
2094 return 0;
2095 }
2096
2097 this._checkingPlaying = true;
2098
2099 var that = this;
2100
2101 //Counts UNCHECKED objects:
2102 var uncheckedObjects = 0;
2103 this.executeFunctionAll
2104 (
2105 function()
2106 {
2107 if (this.getStatus() === CB_AudioFile.UNCHECKED) { uncheckedObjects++; }
2108 }
2109 );
2110
2111 if (uncheckedObjects === 0)
2112 {
2113 if (typeof(callbackOk) === "function") { callbackOk.call(this, 0, 0); }
2114 this._checkingPlaying = false;
2115 return 0;
2116 }
2117
2118 var performedActions = 0;
2119 var errorsHappenend = 0;
2120 var lastError = "";
2121
2122 //Function to execute when an object fails to be checked:
2123 var callbackErrorFunction = function(thisObject, error)
2124 {
2125 that.removeAudioFile(thisObject, true, that.checkManuallyOnCheckingFailed);
2126 errorsHappenend++;
2127 lastError = error;
2128 if (performedActions + errorsHappenend >= uncheckedObjects)
2129 {
2130 that.clearAudioFiles();
2131 if (typeof(callbackError) === "function")
2132 {
2133 callbackError.call(that, "Not all objects could be checked. Message: " + error, performedActions, uncheckedObjects);
2134 callbackError = null; //Prevents executing again.
2135 }
2136 that._checkingPlaying = false;
2137 }
2138 };
2139
2140
2141 //Function to execute when an object checks successfully:
2142 var callbackOkFunction =
2143 function(indexObject, thisObject)
2144 {
2145 //If it is LOADED or LOADING but its internal object is LOADED (would happen when changing API), marks the object as free and increments the pointer:
2146 if (thisObject.getStatus() === CB_AudioFile.LOADED || thisObject.getStatus() === CB_AudioFile.LOADING &amp;&amp; thisObject.getStatus(true) === CB_AudioFile.LOADED)
2147 {
2148 //Marks the object as free and increments the pointer:
2149 that.audioFilesFree[++that.audioFilesFreePointer] = indexObject; //Also increases the pointer ("x" is the position of the object in the array).
2150
2151 //Increases the files created counter:
2152 that.audioFilesCreated++;
2153
2154 performedActions++;
2155
2156 //Stores the minimum and maximum duration found:
2157 var duration = thisObject.getDuration();
2158 if (duration > 0 &amp;&amp; (that.duration === 0 || duration &lt; that.duration)) { that.duration = duration; }
2159 if (duration > that.durationMaximum) { that.durationMaximum = duration; }
2160
2161 if (performedActions >= uncheckedObjects)
2162 {
2163 that.clearAudioFiles();
2164 if (that.status === CB_AudioFileCache.UNCHECKED) { that.status = CB_AudioFileCache.LOADED; }
2165 if (typeof(callbackOk) === "function")
2166 {
2167 callbackOk.call(that, performedActions, uncheckedObjects);
2168 callbackOk = null; //Prevents executing again (although is very unlikely).
2169 }
2170 that._checkingPlaying = false;
2171 }
2172 else if (performedActions + errorsHappenend >= uncheckedObjects)
2173 {
2174 that.clearAudioFiles();
2175 if (typeof(callbackError) === "function")
2176 {
2177 callbackError.call(that, "Not all objects could be checked. Message: " + lastError, performedActions, uncheckedObjects);
2178 callbackError = null; //Prevents executing again.
2179 }
2180 that._checkingPlaying = false;
2181 }
2182 }
2183 //...otherwise, if it is FAILED or ABORTED, calls the error functionn:
2184 else if (audioObject.getStatus() === CB_AudioFile.FAILED || audioObject.getStatus() === CB_AudioFile.ABORTED)
2185 {
2186 callbackErrorFunction("The object status is not LOADED or is not LOADING and internal object LOADED (status: " + audioObject.getStatus() + ", status internal object: " + audioObject.getStatus(true) + ") after checking successfully.");
2187 }
2188 };
2189
2190 //Executes checkPlaying for all UNCHECKED objects:
2191 this.executeFunctionAll
2192 (
2193 function(object, indexObject)
2194 {
2195 if (this.getStatus() === CB_AudioFile.UNCHECKED)
2196 {
2197 var thisObject = this;
2198
2199 //Tries to play the sound:
2200 this.checkPlaying
2201 (
2202 function() { callbackOkFunction(indexObject, thisObject); },
2203 function(error) { callbackErrorFunction(thisObject, error); },
2204 false,
2205 ignoreQueue,
2206 false
2207 );
2208 }
2209 }
2210 );
2211
2212 return uncheckedObjects;
2213}
2214
2215
2216/**
2217 * Tries to play all the {@link CB_AudioFile} objects by calling their {@link CB_AudioFile#play} method internally. If a {@link CB_AudioFile} object cannot be played and it is determined necessary, it will try to reload it internally (by calling the {@link CB_AudioFileCache#removeAudioFile} method). It does not create sound instances.
2218 * @function
2219 * @param {number} [startAt=0 | {@link CB_AudioFile_API.WAAPI#lastStartAt} | {@link CB_AudioFile_API.SM2#lastStartAt} | {@link CB_AudioFile_API.ACMP#lastStartAt} | {@link CB_AudioFile_API.AAPI#lastStartAt} | stopAt] - Time in milliseconds where we want the audio to start at. If not provided or it is not a valid number, it will use zero (0) as default which belongs to the beginning of the audio. If the value provided is greater than the "stopAt" provided, it will use the value set in the "lastStartAt" property of the used audio API object (which belongs to the "startAt" value the last time that the "play" method was called). If, even using the "lastStartAt" value is still greater than the "stopAt" provided, it will use the same value as the "stopAt" which means it will not play and will stop immediately. Used internally as the "startAt" parameter to call the {@link CB_AudioFile#play} method of the {@link CB_AudioFile} object.
2220 * @param {number} [stopAt={@link CB_AudioFile_API.WAAPI#getDuration}() | {@link CB_AudioFile_API.SM2#getDuration}() | {@link CB_AudioFile_API.ACMP#getDuration}() | {@link CB_AudioFile_API.AAPI#getDuration}()] - Time in milliseconds where we want the audio to stop at. If not provided or it is not a valid number, it will use the returning value of the "getDuration" method of the used audio API object (which should belong to the total duration of the audio, if it was calculated correctly). Used internally as the "stopAt" parameter to call the {@link CB_AudioFile#play} method of the {@link CB_AudioFile} object.
2221 * @param {boolean} [loop={@link CB_AudioFile#loop}] - Sets whether we want to play the audio looping (starting again and again) or just play it once. Note that at the end of each loop the "onStop" function defined will not be called. Used internally as the "loop" parameter to call the {@link CB_AudioFile#play} method of the {@link CB_AudioFile} object.
2222 * @param {number} [volume=CB_Configuration.CrossBase.CB_AudioFile_AudioFileCache_USE_SPEAKER_VOLUME_AS_DEFAULT ? CB_Speaker.getVolume() : CB_Configuration.CrossBase.CB_Speaker_DEFAULT_VOLUME] - Desired volume to play the audio. Used internally as the "volume" parameter to call the {@link CB_AudioFile#setVolume} method of the {@link CB_AudioFile} object, before playing it.
2223 * @param {boolean} [avoidDelayedPlay=false] - If set to false (recommended) and the audio failed previously or was aborted (destroyed), it will try to load it correctly again automatically and play it after that if possible (this can take some time so the audio could start playing after a delay). Otherwise, if set to true and the audio failed or was aborted (destroyed), the audio will not play at all and the "stop" method of the audio file object will be called immediately. Used internally as the "avoidDelayedPlay" parameter to call the {@link CB_AudioFile#play} method of the {@link CB_AudioFile} object.
2224 * @param {boolean} [allowedRecursiveDelay={@link CB_Configuration.CrossBase.CB_AudioFile_AudioFileCache_ALLOWED_RECURSIVE_DELAY_DEFAULT}] - The maximum amount of time (in milliseconds) of delay that we accept before start playing the audio. If the amount of time is overcome, the audio will not play at all and the "stop" method will be called immediately. Used only when the "avoidDelayedPlay" parameter is set to false and the audio needs to be loaded because it failed previously or was aborted (destroyed). Used internally as the "allowedRecursiveDelay" parameter to call the {@link CB_AudioFile#play} method of the {@link CB_AudioFile} object.
2225 * @param {function} [onPlayStart] - Function to be called when the audio starts playing successfully. The function will be called with the following parameters (in order): "startAt", "stopAt", "startAtNextLoop", "loop", "avoidDelayedPlay", "allowedRecursiveDelay" and "startPlayingTime", being "this" the {@link CB_AudioFile} object. Used internally as the "onPlayStart" parameter to call the {@link CB_AudioFile#play} method of the {@link CB_AudioFile} object.
2226 * @param {function} [onStop] - Function that will be called each time that a {@link CB_AudioFile} object stops playing. Used internally as the "callbackFunction" parameter to call the {@link CB_AudioFile#onStop} method of the {@link CB_AudioFile} object, before playing it.
2227 * @param {boolean} [includingPlaying=false] - If set to true, it will call the {@link CB_AudioFile#play} method even for those {@link CB_AudioFile} objects which are currently playing.
2228 * @returns {integer} Returns the number of {@link CB_AudioFile} objects whose {@link CB_AudioFile#play} method did not return the value of "-1" (this does not mean necessarily that they could be played successfully).
2229 * @todo Think about implementing an "audioFiles" parameter.
2230 */
2231CB_AudioFileCache.prototype.playAll = function(startAt, stopAt, loop, volume, avoidDelayedPlay, allowedRecursiveDelay, onPlayStart, onStop, includingPlaying)
2232{
2233 //Takes all objects out of the array of the free ones:
2234 this.audioFilesFree = [];
2235 this.audioFilesFreePointer = -1;
2236
2237 var that = this;
2238
2239 //Executes play for all:
2240 var performed = 0;
2241 this.executeFunctionAll
2242 (
2243 function(object, indexObject)
2244 {
2245 if (includingPlaying || !this.isPlaying())
2246 {
2247 var thisObject = this;
2248
2249 var statusBefore = thisObject.getStatus();
2250
2251 //Function that removes the sound (executed when play method fails to reload it or when sound is FAILED or ABORTED):
2252 this.onStop
2253 (
2254 function()
2255 {
2256 that._onStopDefaultFunction.call(that, indexObject, thisObject, statusBefore);
2257 if (typeof(onStop) === "function") { onStop.call(thisObject); }
2258 },
2259 false //Does not keep any possibly existing onStop function.
2260 );
2261
2262 ////////////this.onStop(onStop, true); //Keeps the previous function (which marks object as free when it stops).
2263
2264 this.setVolume(volume, true); //Forces to set volume property because unchecked objects which use WAAPI still does not have gainNode created.
2265
2266 //Tries to play the sound:
2267 var played = this.play(startAt, stopAt, loop, avoidDelayedPlay, allowedRecursiveDelay, onPlayStart, function() { that.removeAudioFile(thisObject, true, that.checkManuallyOnPlayingFailed); });
2268
2269 //If the sound has not been played:
2270 if (played === -1)
2271 {
2272 //If the sound is FAILED or ABORTED, removes it:
2273 if (this.getStatus() === CB_AudioFile.FAILED || this.getStatus() === CB_AudioFile.ABORTED) { that.removeAudioFile(this, true, that.checkManuallyOnPlayingFailed); }
2274 }
2275 else { performed++; }
2276 }
2277 }
2278 );
2279
2280 return performed;
2281}
2282
2283
2284/**
2285 * Tries to stops all the existing {@link CB_AudioFile} objects or the desired ones (if provided), which are being played, by calling their {@link CB_AudioFile#stop} method internally.
2286 * @function
2287 * @param {array} [audioFiles={@link CB_AudioFileCache#audioFiles}] - A numeric array containing the {@link CB_AudioFile} objects that we want to affect. It should contain only {@link CB_AudioFile} objects which are already in the current audio file cache. If not provided, it will use all the {@link CB_AudioFile} objects contained in the cache.
2288 * @returns {integer} Returns the number of calls to the {@link CB_AudioFile#stop} method that were performed (which should be the same number as the {@link CB_AudioFile} objects in the "audioFiles" parameter).
2289 */
2290CB_AudioFileCache.prototype.stopAll = function(audioFiles)
2291{
2292 return this.executeFunctionAll(function() { this.stop(); }, 0, audioFiles);
2293}
2294
2295
2296/**
2297 * Plays silently and stops all {@link CB_AudioFile} objects after a desired time. It can be useful for some clients which need the {@link CB_AudioFile#play} method to be called through a user-driven event (as onClick, onTouch, etc.). Internally, it calls {@link CB_AudioFileCache#playAll}(0, null, false, 0, true, null, null, null, includingPlaying) and, after a desired delay, calls the {@link CB_AudioFileCache#stopAll} method.
2298 * @function
2299 * @param {boolean} [includingPlaying=false] - If set to true, it will call the {@link CB_AudioFile#play} method even for those {@link CB_AudioFile} objects which are currently playing.
2300 * @param {number} [delayBeforeStop=100] - Delay (in milliseconds) before stopping the audio, that will be used as the second parameter of the [setTimeout]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout} function when calling the {@link CB_AudioFileCache#stopAll} method.
2301 * @returns {integer} Returns the number of {@link CB_AudioFile} objects whose {@link CB_AudioFile#play} method did not return the value of "-1" (this does not mean necessarily that they could be played successfully).
2302 * @todo Think about implementing an "audioFiles" parameter.
2303 */
2304CB_AudioFileCache.prototype.playAndStopAll = function(includingPlaying, delayBeforeStop)
2305{
2306 if (typeof(delayBeforeStop) === "undefined" || delayBeforeStop === null || isNaN(delayBeforeStop) || delayBeforeStop &lt; 0) { delayBeforeStop = 100; }
2307 var played = this.playAll(0, null, false, 0, true, null, null, null, includingPlaying); //Plays silently.
2308 var that = this;
2309 setTimeout(function() { that.stopAll.call(that); }, delayBeforeStop); //Stops the sound after the delay desired.
2310 return played;
2311}
2312
2313
2314/**
2315 * Tries to pause all the existing {@link CB_AudioFile} objects or the desired ones (if provided), which are being played, by calling their {@link CB_AudioFile#pause} method internally.
2316 * @function
2317 * @param {function} [onPause] - Function without parameters to be called when the audio is paused successfully, being "this" the {@link CB_AudioFile} object. Used internally as the "onPause" parameter to call the {@link CB_AudioFile#pause} method of the {@link CB_AudioFile} object.
2318 * @param {array} [audioFiles={@link CB_AudioFileCache#audioFiles}] - A numeric array containing the {@link CB_AudioFile} objects that we want to affect. It should contain only {@link CB_AudioFile} objects which are already in the current audio file cache. If not provided, it will use all the {@link CB_AudioFile} objects contained in the cache.
2319 * @returns {integer} Returns the number of calls to the {@link CB_AudioFile#pause} method that were performed (which should be the same number as the {@link CB_AudioFile} objects in the "audioFiles" parameter).
2320 */
2321CB_AudioFileCache.prototype.pauseAll = function(onPause, audioFiles)
2322{
2323 return this.executeFunctionAll(function() { this.pause(onPause); }, 0, audioFiles);
2324}
2325
2326
2327/**
2328 * Resumes all the existing {@link CB_AudioFile} objects or the desired ones (if provided), which are paused (and not stopped). It uses the {@link CB_AudioFileCache#play} method internally.
2329 * @function
2330 * @param {boolean} [loop={@link CB_AudioFile#loop}] - Used internally as the "loop" parameter to call the {@link CB_AudioFileCache#play} method.
2331 * @param {boolean} [allowedRecursiveDelay={@link CB_Configuration.CrossBase.CB_AudioFile_AudioFileCache_ALLOWED_RECURSIVE_DELAY_DEFAULT}] - Used internally as the "allowedRecursiveDelay" parameter to call the {@link CB_AudioFileCache#play} method.
2332 * @param {boolean} [allowedRecursiveDelaySkipping=CB_AudioFile#lastStopAt-CB_AudioFile#lastStartAt] - Used internally as the "allowedRecursiveDelaySkipping" parameter to call the {@link CB_AudioFileCache#play} method.
2333 * @param {function} [onPlayStart] - Used internally as the "onPlayStart" parameter to call the {@link CB_AudioFileCache#play} method.
2334 * @param {function} [onStop] - Used internally as the "onStop" parameter to call the {@link CB_AudioFileCache#play} method.
2335 * @param {array} [audioFiles={@link CB_AudioFileCache#audioFiles}] - A numeric array containing the {@link CB_AudioFile} objects that we want to affect. It should contain only {@link CB_AudioFile} objects which are already in the current audio file cache. If not provided, it will use all the {@link CB_AudioFile} objects contained in the cache.
2336 * @returns {array} Returns a numeric array containing all the return values of each internal call to the {@link CB_AudioFileCache#play} method.
2337 */
2338CB_AudioFileCache.prototype.resumeAll = function(loop, allowedRecursiveDelay, allowedRecursiveDelaySkipping, onPlayStart, onStop, audioFiles)
2339{
2340 var that = this;
2341 var startAt;
2342 var startAtOriginal;
2343 var soundInstances = [];
2344 //var soundInstance;
2345 this.executeFunctionAll
2346 (
2347 function()
2348 {
2349 if (this.isPaused() &amp;&amp; !this.isStopped())
2350 {
2351 //Gets the data needed for the resume:
2352 startAt = this.audioFileObject.pauseTime;
2353 startAtOriginal = this.audioFileObject.lastStartAt;
2354
2355 if (startAt >= this.audioFileObject.lastStopAt)
2356 {
2357 startAt = this.audioFileObject.lastStopAt - 1; //We will begin just 1 millisecond before (otherwise the play method would begin again from lastStartAt).
2358 }
2359
2360 //Maybe the object which will be played (resumed) will not be this one, so we stop it and declare it as not paused (no one paused should remain):
2361 this._fireOnStopByUser = false;
2362 this.stop(false, false, true); //This way we also make it free (firing onStop).
2363 this._fireOnStopByUser = true;
2364
2365 //var loopSet = loop;
2366 //if (typeof(loopSet) === "undefined" || loopSet === null) { loopSet = this.loop; } //By default, uses the previous loop used.
2367 if (typeof(loop) === "undefined" || loop === null) { loop = this.loop; } //By default, uses the previous loop used.
2368
2369 //Plays a sound (maybe not the same object) which will simulate a resume with the data we have:
2370 soundInstances[soundInstances.length] = that.play(startAt, this.audioFileObject.lastStopAt, loop /*loopSet*/, this.audioFileObject.volumeBeforeMute, allowedRecursiveDelay, allowedRecursiveDelaySkipping, onPlayStart, onStop, null, startAtOriginal);
2371 //this.resume(loop, null, avoidDelayedPlay, allowedRecursiveDelay, onPlayStart, function() { that.removeAudioFile(thisObject, true); });
2372
2373 //soundInstances[soundInstances.length] = soundInstance;
2374 }
2375 },
2376 0,
2377 audioFiles
2378 );
2379 return soundInstances;
2380}
2381
2382
2383/**
2384 * Mutes all the existing {@link CB_AudioFile} objects or the desired ones (if provided). It uses the {@link CB_AudioFile#mute} method internally.
2385 * @function
2386 * @param {function} [onMute] - Callback function which will be called for each audio file if it has been possible to mute it (or at least it was possible to try it), being "this" the {@link CB_AudioFile} object. Used internally as the "onMute" parameter to call the {@link CB_AudioFile#mute} method.
2387 * @param {array} [audioFiles={@link CB_AudioFileCache#audioFiles}] - A numeric array containing the {@link CB_AudioFile} objects that we want to affect. It should contain only {@link CB_AudioFile} objects which are already in the current audio file cache. If not provided, it will use all the {@link CB_AudioFile} objects contained in the cache.
2388 * @returns {integer} Returns the number of calls to the {@link CB_AudioFile#mute} method that were performed (which should be the same number as the {@link CB_AudioFile} objects in the "audioFiles" parameter).
2389 */
2390CB_AudioFileCache.prototype.muteAll = function(onMute, audioFiles)
2391{
2392 return this.executeFunctionAll(function() { this.mute(onMute); }, 0, audioFiles);
2393}
2394
2395
2396/**
2397 * Unmutes all the existing {@link CB_AudioFile} objects or the desired ones (if provided). It uses the {@link CB_AudioFile#unmute} method internally.
2398 * @function
2399 * @param {function} [onUnmute] - Callback function which will be called for each audio file if it has been possible to unmute it (or at least it was possible to try it), being "this" the {@link CB_AudioFile} object. Used internally as the "onUnmute" parameter to call the {@link CB_AudioFile#unmute} method.
2400 * @param {array} [audioFiles={@link CB_AudioFileCache#audioFiles}] - A numeric array containing the {@link CB_AudioFile} objects that we want to affect. It should contain only {@link CB_AudioFile} objects which are already in the current audio file cache. If not provided, it will use all the {@link CB_AudioFile} objects contained in the cache.
2401 * @returns {integer} Returns the number of calls to the {@link CB_AudioFile#unmute} method that were performed (which should be the same number as the {@link CB_AudioFile} objects in the "audioFiles" parameter).
2402 */
2403CB_AudioFileCache.prototype.unmuteAll = function(onUnmute, audioFiles)
2404{
2405 return this.executeFunctionAll(function() { this.unmute(onUnmute); }, 0, audioFiles);
2406}
2407
2408
2409/**
2410 * Sets the same volume for all the existing {@link CB_AudioFile} objects or the desired ones (if provided). It uses the {@link CB_AudioFile#setVolume} method internally.
2411 * @function
2412 * @param {number} [volume={@link CB_Speaker.getVolume()} | {@link CB_Configuration.CrossBase.CB_Speaker_DEFAULT_VOLUME}] - Desired volume (from 0 to the maximum value, where the maximum value will be the returning value of calling the {@link CB_Speaker.getVolume} function if the {@link CB_Configuration.CrossBase.CB_AudioFile_AudioFileCache_USE_SPEAKER_VOLUME_AS_MAXIMUM} property is set to true or it will be 100 otherwise). Used internally as the "volume" parameter to call the {@link CB_AudioFile#setVolume} method.
2413 * @param {boolean} [forceSetVolumeProperty=false] - If set to true (not recommended), it will change the "volume" property of the used audio API object even when the volume was failed to be changed. Used internally as the "forceSetVolumeProperty" parameter to call the {@link CB_AudioFile#setVolume} method.
2414 * @param {function} [onSetVolume] - Callback function which will be called if it has been possible to set the volume (or at least it was possible to try it), being "this" the {@link CB_AudioFile} object. Used internally as the "onSetVolume" parameter to call the {@link CB_AudioFile#setVolume} method.
2415 * @param {array} [audioFiles={@link CB_AudioFileCache#audioFiles}] - A numeric array containing the {@link CB_AudioFile} objects that we want to affect. It should contain only {@link CB_AudioFile} objects which are already in the current audio file cache. If not provided, it will use all the {@link CB_AudioFile} objects contained in the cache.
2416 * @returns {integer} Returns the number of calls to the {@link CB_AudioFile#setVolume} method that were performed (which should be the same number as the {@link CB_AudioFile} objects in the "audioFiles" parameter).
2417 */
2418CB_AudioFileCache.prototype.setVolumeAll = function(volume, forceSetVolumeProperty, onSetVolume, audioFiles)
2419{
2420 return this.executeFunctionAll(function() { this.setVolume(volume, forceSetVolumeProperty, onSetVolume); }, 0, audioFiles);
2421}
2422
2423
2424/**
2425 * Callback function used by the {@link CB_AudioFileCache#setAudioAPIAll} method that will be called when all the process was performed successfully.
2426 * @memberof CB_AudioFileCache
2427 * @callback CB_AudioFileCache.setAudioAPIAll_CALLBACK_OK
2428 * @param {integer} objectsChangedAPI - The number of {@link CB_AudioFile} objects that actually changed its audio API.
2429 * @param {integer} performedActions - The number of {@link CB_AudioFile} objects that ended with a desired audio API, including those ones which were already using it.
2430 * @param {integer} actionsNeeded - The total number of {@link CB_AudioFile} objects that were considered to perform the action (it will be undefined if it could not be determined).
2431 */
2432
2433 /**
2434 * Callback function used by the {@link CB_AudioFileCache#setAudioAPIAll} method that will be called when any error happened.
2435 * @memberof CB_AudioFileCache
2436 * @callback CB_AudioFileCache.setAudioAPIAll_CALLBACK_ERROR
2437 * @param {string} error - A string describing the error, if it was possible to be determined.
2438 * @param {integer} errorsHappened - The number of errors that happened, which could be greater than 1 if more than one internal call to the {@link CB_AudioFile#setAudioAPI} method failed.
2439 * @param {integer} objectsChangedAPI - The number of {@link CB_AudioFile} objects that actually changed its audio API.
2440 * @param {integer} performedActions - The number of {@link CB_AudioFile} objects that ended with a desired audio API, including those ones which were already using it.
2441 * @param {integer} actionsNeeded - The total number of {@link CB_AudioFile} objects that were considered to perform the action (it will be undefined if it could not be determined).
2442 */
2443
2444 /**
2445 * Tries to change the audio API for all the existing {@link CB_AudioFile} objects or the desired ones (if provided). Uses the {@link CB_AudioFile#setAudioAPI} method internally. This method is not allowed to be called if a previous call to it did not finish yet. The function defined in the "callbackError" parameter, if any, will be called immediately if the method was previously called and it is still running currently.
2446 * @function
2447 * @param {array|string} preferredAPIs - Array of strings with the preferred audio API or audio APIs, in order of preference. It also accepts a string with only one audio API. If more than one audio API is provided and setting an audio API fails for a {@link CB_AudioFile} object, it will try setting the next one and so on (this means that some of the {@link CB_AudioFile} objects could end with a different audio API). Possible audio APIs are "WAAPI" ([HTML5 Web Audio API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API}), "SM2" ([SoundManager 2]{@link http://schillmania.com/projects/soundmanager2/}), "ACMP" ([Apache Cordova Media Plugin]{@link https://github.com/apache/cordova-plugin-media}) or "AAPI" ([HTML5 Audio API]{@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/audio}).
2448 * @param {CB_AudioFileCache.setAudioAPIAll_CALLBACK_OK} [callbackOk] - Function that will be called when all the process was performed successfully, being "this" the {@link CB_AudioFileCache} object.
2449 * @param {CB_AudioFileCache.setAudioAPIAll_CALLBACK_ERROR} [callbackError] - Function that will be called when any error happened, being "this" the {@link CB_AudioFileCache} object. This function will be called immediately if the method was previously called and it is still running currently.
2450 * @param {boolean} [mandatory=false] - If set to true and any {@link CB_AudioFile} object could not perform successfully any call to its {@link CB_AudioFile#setAudioAPI} method for all desired audio APIs provided in the "preferredAPIs" parameter (this means that, internally, all the {@link CB_AudioFile#setAudioAPI} calls, one per desired audio API, have fired an error by calling the function defined in its "callbackError" parameter), the audio file cache will be set as "FAILED" (the {@link CB_AudioFileCache#status} property will be set to the value of {@link CB_AudioFileCache.FAILED}).
2451 * @param {string} [forceReload=false] - Used internally as the "forceReload" parameter when calling the {@link CB_AudioFile#setAudioAPI} method internally.
2452 * @param {array} [audioFiles={@link CB_AudioFileCache#audioFiles}] - A numeric array containing the {@link CB_AudioFile} objects that we want to affect. It should contain only {@link CB_AudioFile} objects which are already in the current audio file cache. If not provided, it will use all the {@link CB_AudioFile} objects contained in the cache.
2453 * @returns {integer} Returns the number of calls to the {@link CB_AudioFile#setAudioAPI} method that were performed (which should be the same number as the {@link CB_AudioFile} objects in the "audioFiles" parameter).
2454 */
2455CB_AudioFileCache.prototype.setAudioAPIAll = function(preferredAPIs, callbackOk, callbackError, mandatory, forceReload, audioFiles)
2456{
2457 //No sound object should be free now (because all are changing their API):
2458 ///////this.audioFilesFree = [];
2459 ///////this.audioFilesFreePointer = -1;
2460
2461 if (this._settingAPI)
2462 {
2463 if (typeof(callbackError) === "function")
2464 {
2465 callbackError.call(this, "Method setAudioAPIAll cannot be executed again until it finishes.", 1, 0, 0, undefined);
2466 }
2467 return 0;
2468 }
2469
2470 this._settingAPI = true;
2471
2472 var that = this;
2473
2474 //Function to execute when all APIs fail to apply to a single object:
2475 var objectsChangedAPI = 0;
2476 var errorsHappened = 0;
2477 var errorFunction =
2478 function(error, avoidFailing, callErrorFunction)
2479 {
2480 errorsHappened++;
2481
2482 //If all actions have performed (fine or with an error), sets the cache as LOADED and clears the array:
2483 if (performedActions + errorsHappened >= actionsNeeded)
2484 {
2485 that.status = CB_AudioFileCache.LOADED;
2486 that.clearAudioFiles();
2487 callErrorFunction = true;
2488 }
2489
2490 //If it was mandatory and we do not want to avoid failing, sets the cache as FAILED:
2491 if (mandatory &amp;&amp; !avoidFailing) { that.status = CB_AudioFileCache.FAILED; }
2492
2493 //Calls the error function (if any):
2494 if (callErrorFunction)
2495 {
2496 if (typeof(callbackError) === "function")
2497 {
2498 callbackError.call(that, error, errorsHappened, objectsChangedAPI, performedActions, actionsNeeded);
2499 callbackError = null; //Prevents calling the function again.
2500 }
2501 that._settingAPI = false;
2502 }
2503 };
2504
2505 var performedActions = 0;
2506 var actionsNeeded = undefined;
2507
2508 //If no preferred APIs are sent, throws an error and exits:
2509 if (typeof(preferredAPIs) === "undefined" || preferredAPIs === null) { errorFunction("No APIs given.", true, true); return 0; } //Avoid setting status to FAILED.
2510 //...otherwise, if the API sent is not an array, turns it into an array:
2511 else if (!CB_isArray(preferredAPIs)) { preferredAPIs = [CB_trim(preferredAPIs)]; }
2512
2513 //Filters the audio APIs to just use the supported ones:
2514 preferredAPIs = CB_AudioDetector.getSupportedAPIs(preferredAPIs);
2515 //If preferredAPIs is empty, calls the OK function and exits:
2516 //if (preferredAPIs.length === 0) { callbackOkFunction(null, true); return 0; }
2517 //If preferredAPIs is empty, calls the error function and exits:
2518 if (preferredAPIs.length === 0) { errorFunction("No APIs supported from the provided ones.", true, true); return 0; }
2519
2520 //If the URI list has no elements, exits:
2521 if (typeof(this._URIsListLast) === "undefined" || !CB_isArray(this._URIsListLast) || this._URIsListLast.length === 0) { errorFunction("URIs list is undefined or empty.", true, true); return 0; }
2522
2523 //If the cache status is not LOADED, exits:
2524 if (this.status !== CB_AudioFileCache.LOADED) { errorFunction("Cache is not loaded.", true, true); return 0; }
2525
2526 //Defines how many actions will have to be performed:
2527
2528 if (typeof(audioFiles) === "undefined" || audioFiles === null || !CB_isArray(audioFiles)) { audioFiles = this.audioFiles; }
2529 else if (audioFiles.length === 0)
2530 {
2531 if (typeof(callbackOk) === "function") { callbackOk.call(this, 0, 0, undefined); }
2532 this._settingAPI = false;
2533 return 0;
2534 }
2535
2536 this.status = CB_AudioFileCache.LOADING; //The cache is loading.
2537
2538 actionsNeeded = 0;
2539 var audioFilesLength = audioFiles.length;
2540 for (var x = 0; x &lt; audioFilesLength; x++)
2541 {
2542 //If the object exists:
2543 if (typeof(audioFiles[x]) !== "undefined" &amp;&amp; audioFiles[x] !== null)
2544 {
2545 actionsNeeded++;
2546 }
2547 }
2548
2549 //Function to execute when the audio object has changed the API successfully:
2550 var callbackOkFunction =
2551 function(indexObject, sameAPI)
2552 {
2553 if (!sameAPI) { objectsChangedAPI++; }
2554
2555 //The audio object is free now:
2556 ///////that.audioFilesFree[++that.audioFilesFreePointer] = indexObject;
2557
2558 //Increments the counter of objects that have performed the action well:
2559 performedActions++;
2560
2561 //Stores the minimum and maximum duration found:
2562 var duration = this.getDuration();
2563 if (duration > 0 &amp;&amp; (that.duration === 0 || duration &lt; that.duration)) { that.duration = duration; }
2564 if (duration > that.durationMaximum) { that.durationMaximum = duration; }
2565
2566 //If all objects have performed their action well:
2567 if (performedActions >= actionsNeeded)
2568 {
2569 that.clearAudioFiles();
2570
2571 //The cache finished loading:
2572 that.status = CB_AudioFileCache.LOADED;
2573
2574 //We call the OK function (if any):
2575 if (typeof(callbackOk) === "function")
2576 {
2577 callbackOk.call(that, objectsChangedAPI, performedActions, actionsNeeded);
2578 callbackOk = null; //Prevents calling the function again.
2579 }
2580 that._settingAPI = false;
2581 }
2582 //...otherwise, if all actions have performed (fine or with an error), sets the cache as LOADED and clears the array:
2583 else if (performedActions + errorsHappened >= actionsNeeded)
2584 {
2585 that.status = CB_AudioFileCache.LOADED;
2586 that.clearAudioFiles();
2587
2588 //If it was mandatory, sets the cache as FAILED:
2589 if (mandatory) { that.status = CB_AudioFileCache.FAILED; }
2590
2591 //Calls the error function (if any):
2592 if (typeof(callbackError) === "function")
2593 {
2594 callbackError.call(that, error, errorsHappened, objectsChangedAPI, performedActions, actionsNeeded);
2595 callbackError = null; //Prevents calling the function again.
2596 }
2597 that._settingAPI = false;
2598 }
2599 };
2600
2601 //Function to execute when the audio object has failed to change the API:
2602 var callbackErrorFunction =
2603 function(error, indexObject, preferredAPIs, URIsList, URIsIndex, currentRetry, APIsTried)
2604 {
2605 /////that.clearAudioFiles();
2606
2607 var thisObject = this;
2608
2609 //If it is the last URI and last API we can try, fails:
2610 if (URIsIndex + 1 >= URIsList.length &amp;&amp; preferredAPIs.length &lt;= 1)
2611 {
2612 thisObject.settingAPI = false;
2613
2614 //Calls the error function:
2615 errorFunction(error);
2616 return;
2617 }
2618 //...otherwise, continues trying the next desired API:
2619 else
2620 {
2621 //If the desired number of retries have been executed, passes to the next URI:
2622 if (++currentRetry > that.retries)
2623 {
2624 currentRetry = 0;
2625 //If all URIs have been tried, passes to the next API:
2626 if (++URIsIndex >= URIsList.length)
2627 {
2628 URIsIndex = 0;
2629 preferredAPIs = preferredAPIs.slice(1); //Takes out the first API (the one we have already tried).
2630 }
2631 }
2632 //Calls the function again to try again:
2633 setTimeout
2634 (
2635 function()
2636 {
2637 setAPIEach.call(thisObject, thisObject, indexObject, undefined, undefined, preferredAPIs, URIsList, URIsIndex, currentRetry, APIsTried);
2638 },
2639 (currentRetry === 0) ? 100 : 1000 //Delay is bigger if we are still trying the same API (to avoid problems with Firefox and AAPI or SM2).
2640 );
2641 }
2642 };
2643
2644
2645 /////this.clearAudioFiles();
2646
2647 //Function to execute to change API for a single object:
2648 var setAPIEach =
2649 function(object, indexObject, array, delay, preferredAPIsLocal, URIsList, URIsIndex, currentRetry, APIsTried)
2650 {
2651 if (typeof(preferredAPIsLocal) === "undefined" || preferredAPIsLocal === null) { preferredAPIsLocal = preferredAPIs; }
2652
2653 if (typeof(URIsList) === "undefined" || URIsList === null) { URIsList = that._URIsListLast; }
2654 if (typeof(URIsIndex) === "undefined" || URIsIndex === null) { URIsIndex = 0; }
2655 if (typeof(currentRetry) === "undefined" || currentRetry === null) { currentRetry = 0; }
2656 if (typeof(APIsTried) === "undefined" || APIsTried === null) { APIsTried = []; }
2657
2658 //If the API used is already being used, just calls the OK function and exits:
2659 if (this.audioAPI === preferredAPIsLocal[0]) { callbackOkFunction.call(this, indexObject, true); return; }
2660
2661 //If it is the first time we try the API:
2662 if (CB_indexOf(APIsTried, preferredAPIsLocal[0]) === -1)
2663 {
2664 //Stores the new API that we are going to try:
2665 APIsTried[APIsTried.length] = preferredAPIsLocal[0];
2666
2667 //If the current object has already loaded the current API before, gets its URI:
2668 if (typeof(this.audioFileObjects) !== "undefined" &amp;&amp; typeof(this.audioFileObjects[preferredAPIsLocal[0]]) !== "undefined")
2669 {
2670 if (typeof(this.audioFileObjects[preferredAPIsLocal[0]].status) !== "undefined" &amp;&amp; this.audioFileObjects[preferredAPIsLocal[0]].status === CB_AudioFile.LOADED)
2671 {
2672 if (typeof(this.audioFileObjects[preferredAPIsLocal[0]].filePath) !== "undefined" &amp;&amp; this.audioFileObjects[preferredAPIsLocal[0]].filePath !== null)
2673 {
2674 var firstURI = this.audioFileObjects[preferredAPIsLocal[0]].filePath;
2675
2676 //If the currently used URI is in the URIs list:
2677 if (CB_indexOf(URIsList, firstURI) !== -1)
2678 {
2679 //Places the URI used at the beginning:
2680 var URIsListNew = [];
2681 URIsListNew[0] = firstURI;
2682
2683 //Refills the array with the rest of the URIs:
2684 var URIsListLength = URIsList.length;
2685 for (var x = 0; x &lt; URIsListLength; x++)
2686 {
2687 if (URIsList[x] !== firstURI)
2688 {
2689 URIsListNew[URIsListNew.length] = URIsList[x];
2690 }
2691 }
2692
2693 //Saves the new order:
2694 URIsList = URIsListNew;
2695 }
2696 }
2697 }
2698 }
2699 }
2700
2701 var thisObject = this;
2702
2703 thisObject.settingAPI = true;
2704
2705 //Tries to change the API:
2706 this.setAudioAPI
2707 (
2708 preferredAPIsLocal[0], //audioAPI.
2709 true, //audoLoad.
2710 false, //autoPlay.
2711 function() { thisObject.settingAPI = false; callbackOkFunction.call(thisObject, indexObject); }, //callbackOk.
2712 function(error) { callbackErrorFunction.call(thisObject, error, indexObject, preferredAPIsLocal, URIsList, URIsIndex, currentRetry, APIsTried); }, //callbackError,
2713 false, //ignoreOldValues.
2714 URIsList[URIsIndex], //filePath.
2715 forceReload //forceReload
2716 );
2717
2718 /////that.clearAudioFiles();
2719 };
2720
2721 //Tries to change the API for all objects:
2722 ///////this.executeFunctionAll(setAPIEach, 10);
2723 //return this.executeFunctionAll(setAPIEach);
2724 return this.executeFunctionAll(setAPIEach, 0, audioFiles);
2725}
2726
2727
2728/**
2729 * Tells whether any of the {@link CB_AudioFile} objects is playing or not. It uses the {@link CB_AudioFile#isPlaying} method internally.
2730 * @function
2731 * @returns {boolean} Returns whether any of the {@link CB_AudioFile} objects is playing or not.
2732 */
2733CB_AudioFileCache.prototype.isPlaying = function()
2734{
2735 var audioFilesLength = this.audioFiles.length;
2736 for (var x = 0; x &lt; audioFilesLength; x++)
2737 {
2738 if (typeof(this.audioFiles[x]) !== "undefined" &amp;&amp; typeof(this.audioFiles[x].isPlaying) !== "undefined" &amp;&amp; this.audioFiles[x].isPlaying())
2739 {
2740 return true;
2741 }
2742 }
2743 return false;
2744}
2745
2746
2747/**
2748 * Tells the current number of free {@link CB_AudioFile} objects (the number of objects which are available and ready to use).
2749 * @function
2750 * @returns {integer} Returns the current number of free {@link CB_AudioFile} objects (the number of objects which are available and ready to use).
2751 */
2752CB_AudioFileCache.prototype.getAudioFilesFreeNumber = function()
2753{
2754 return this.audioFilesFreePointer + 1;
2755}
2756
2757
2758/**
2759 * Gets an array with all the {@link CB_AudioFile} objects.
2760 * @function
2761 * @param {boolean} [copy=false] - If set to true, instead of returning the {@link CB_AudioFileCache#audioFiles} property directly, it will return a new copy of it.
2762 * @returns {array} Returns an array with all the {@link CB_AudioFile} objects.
2763 */
2764CB_AudioFileCache.prototype.getAudioFiles = function(copy)
2765{
2766 if (copy)
2767 {
2768 var audioFiles = [];
2769 var audioFilesLength = this.audioFiles.length;
2770 var y = 0;
2771 for (var x = 0; x &lt; audioFilesLength; x++)
2772 {
2773 if (typeof(this.audioFiles[x]) !== "undefined" &amp;&amp; this.audioFiles[x] !== null)
2774 {
2775 audioFiles[y++] = this.audioFiles[x];
2776 }
2777 }
2778 return audioFiles;
2779 }
2780 else { return this.audioFiles; }
2781}
2782
2783
2784/**
2785 * Gets an array with the free {@link CB_AudioFile} objects (the objects which are available and ready to use).
2786 * @function
2787 * @returns {array} Returns an array with the free {@link CB_AudioFile} objects (the objects which are available and ready to use).
2788 */
2789CB_AudioFileCache.prototype.getAudioFilesFree = function()
2790{
2791 var audioFiles = [];
2792 var audioFilesLength = this.audioFiles.length;
2793 var y = 0;
2794 for (var x = 0; x &lt; audioFilesLength; x++)
2795 {
2796 if (typeof(this.audioFiles[x]) !== "undefined" &amp;&amp; this.audioFiles[x] !== null &amp;&amp; this.isAudioFileFreeByPosition(x)) //"x" is the position.
2797 {
2798 audioFiles[y++] = this.audioFiles[x];
2799 }
2800 }
2801 return audioFiles;
2802}
2803
2804
2805/**
2806 * Gets an array with the busy {@link CB_AudioFile} objects (the objects which are not available and ready to use).
2807 * @function
2808 * @returns {array} Returns an array with the busy {@link CB_AudioFile} objects (the objects which are not available and ready to use).
2809 */
2810CB_AudioFileCache.prototype.getAudioFilesBusy = function()
2811{
2812 var audioFiles = [];
2813 var audioFilesLength = this.audioFiles.length;
2814 var y = 0;
2815 for (var x = 0; x &lt; audioFilesLength; x++)
2816 {
2817 if (typeof(this.audioFiles[x]) !== "undefined" &amp;&amp; this.audioFiles[x] !== null &amp;&amp; !this.isAudioFileFreeByPosition(x)) //"x" is the position.
2818 {
2819 audioFiles[y++] = this.audioFiles[x];
2820 }
2821 }
2822 return audioFiles;
2823}
2824
2825
2826/**
2827 * Tells the number of {@link CB_AudioFile} objects created.
2828 * @function
2829 * @param {boolean} [real=false] - If set to true, instead of returning the value of the {@link CB_AudioFileCache#audioFilesCreated} property, it will return the value of the "length" property of the {@link CB_AudioFileCache#audioFiles} array which are the real number of {@link CB_AudioFile} objects used. If all went well, the returning value should be always the same regardless of this parameter.
2830 * @returns {integer} Returns the number of {@link CB_AudioFile} objects created.
2831 */
2832CB_AudioFileCache.prototype.getAudioFilesNumber = function(real)
2833{
2834 if (real) { return this.audioFiles.length; }
2835 else { return this.audioFilesCreated; }
2836}
2837
2838
2839/**
2840 * Tells the duration (minimum or maximum) of the sound stored (in milliseconds). Although the audio file cache should always be used to cache the same sound only, the duration might not always be the same due the usage of different formats, file paths, etc. So this method returns either the minimum or the maximum duration found among all the {@link CB_AudioFile} objects.
2841 * @function
2842 * @param {boolean} [maximum=false] - If set to true, instead of returning the value of the {@link CB_AudioFileCache#duration} property (which belongs to the minimum duration found among all the {@link CB_AudioFile} objects), it will return the value of {@link CB_AudioFileCache#durationMaximum} property (which belongs to the maximum duration found among all the {@link CB_AudioFile} objects).
2843 * @returns {number} Returns the duration (minimum or maximum) of the sound stored (in milliseconds). Although the audio file cache should always be used to cache the same sound only, the duration might not always be the same due the usage of different formats, file paths, etc. So this method returns either the minimum or the maximum duration found among all the {@link CB_AudioFile} objects.
2844 */
2845CB_AudioFileCache.prototype.getDuration = function(maximum)
2846{
2847 if (maximum) { return this.durationMaximum; }
2848 else { return this.duration; }
2849}
2850
2851
2852/**
2853 * Returns a number representing the percentage of the loading progress for the audio file cache (from 0 to 100, being 100 a complete loading progress). The way to calculate it internally may differ from one audio API to another and it is not totally reliable.
2854 * @function
2855 * @param {boolean} [countLoadedObjects=false] - If set to true, it will count the {@link CB_AudioFile} objects whose {@link CB_AudioFile#getStatus} method returns "LOADED" (the value of the {@link CB_AudioFile#LOADED} property), instead of just using the array's "length" of the {@link CB_AudioFileCache#audioFiles} property.
2856 * @param {boolean} [alsoUncheckedAndCheckingObjects=false] - If set to true and the "countLoadedObjects" parameter is also true, it will also count the {@link CB_AudioFile} objects whose {@link CB_AudioFile#getStatus} method returns "UNCHECKED" (the value of the {@link CB_AudioFile#UNCHECKED} property) or "CHECKING" (the value of the {@link CB_AudioFile#CHECKING} property). If the "countLoadedObjects" parameter is false, this parameter will be ignored.
2857 * @returns {number} Returns a number representing the percentage of the loading progress for the audio file cache (from 0 to 100, being 100 a complete loading progress). The way to calculate it internally may differ from one audio API to another and it is not totally reliable.
2858 * @todo Although it would be more accurate, it does not use the {@link CB_AudioFile#getProgress} method internally because the {@link CB_AudioFile} objects are not added to the {@link CB_AudioFileCache#audioFiles} property until they are loaded. It would be nice to code a way to be able to use it (perhaps a property where the {@link CB_AudioFile} objects loading are kept temporarily).
2859 */
2860CB_AudioFileCache.prototype.getProgress = function(countLoadedObjects, alsoUncheckedAndCheckingObjects)
2861{
2862 //var objectsLoaded = (this.audioFiles.length &lt; this.audioFilesCreated) ? this.audioFiles.length : this.audioFilesCreated; //Takes the smallest number.
2863 var objectsLoaded = this.audioFiles.length;
2864 if (countLoadedObjects)
2865 {
2866 var audioFilesLength = this.audioFiles.length;
2867 var objectsReallyLoaded = 0;
2868 for (var x = 0; x &lt; audioFilesLength; x++)
2869 {
2870 if (typeof(this.audioFiles[x]) !== "undefined" &amp;&amp; typeof(this.audioFiles[x].getStatus) !== "undefined")
2871 //if (typeof(this.audioFiles[x]) !== "undefined" &amp;&amp; typeof(this.audioFiles[x].getStatus) !== "undefined")
2872 {
2873 if (typeof(this.audioFiles[x].settingAPI) === "undefined" || !this.audioFiles[x].settingAPI)
2874 {
2875 if (this.audioFiles[x].getStatus() === CB_AudioFile.LOADED) { objectsReallyLoaded++; }
2876 else if (alsoUncheckedAndCheckingObjects &amp;&amp; (this.audioFiles[x].getStatus() === CB_AudioFile.UNCHECKED || this.audioFiles[x].getStatus() === CB_AudioFile.CHECKING)) { objectsReallyLoaded++; }
2877 }
2878 /*
2879 //LOADED counts as one:
2880 if (this.audioFiles[x].getStatus() === CB_AudioFile.LOADED)
2881 {
2882 objectsReallyLoaded++;
2883 }
2884 //UNCHECKED and CHECKING counts as 50%:
2885 else if (this.audioFiles[x].getStatus() === CB_AudioFile.UNCHECKED || this.audioFiles[x].getStatus() === CB_AudioFile.CHECKING)
2886 {
2887 objectsReallyLoaded += 0.5;
2888 }
2889 //LOADING counts as 50% when the progress is 100%:
2890 else if (this.audioFiles[x].getStatus() === CB_AudioFile.LOADING)
2891 {
2892 objectsReallyLoaded += this.audioFiles[x].getProgress() / 200; //It will be 0.5 when the object is loaded completely (getProgress returns 100).
2893 }
2894 */
2895 }
2896 }
2897 //objectsLoaded = (objectsLoaded &lt; objectsReallyLoaded) ? objectsLoaded : objectsReallyLoaded; //Takes the smallest number.
2898 objectsLoaded = objectsReallyLoaded;
2899 }
2900 var objectsNeeded = this.minimumAudioFiles;
2901 var progress = (objectsLoaded / objectsNeeded) * 100;
2902 if (progress > 100) { progress = 100; }
2903 else if (progress &lt; 0) { progress = 0; }
2904 //if (progress === 100 &amp;&amp; this.status === CB_AudioFileCache.LOADING) { progress = 99.99; }
2905 return progress;
2906}
2907
2908
2909/**
2910 * Gets the current status of the audio file cache.
2911 * @function
2912 * @returns {number} Returns the current status of the audio file cache. It is a number, which should match the value of the {@link CB_AudioFileCache.UNLOADED} (still unloaded), {@link CB_AudioFileCache.LOADING} (loading), {@link CB_AudioFileCache.UNCHECKED} (not checked by calling the {@link CB_AudioFileCache#checkPlayingAll} method yet), {@link CB_AudioFileCache.CHECKING} (being checked by the {@link CB_AudioFileCache#checkPlayingAll} method), {@link CB_AudioFileCache.LOADED} (loaded), {@link CB_AudioFileCache.FAILED} (failed loading or failed to play or by any other reason) or {@link CB_AudioFileCache.ABORTED} (aborted because it was destroyed with the "destructor" method) property.
2913 */
2914CB_AudioFileCache.prototype.getStatus = function()
2915{
2916 return this.status;
2917}
2918
2919
2920/**
2921 * Gets the current status of the audio file cache, as a string.
2922 * @function
2923 * @returns {string} Returns the current status of the audio file cache, as a string. Possible return values are "UNLOADED", "LOADING", "UNCHECKED", "CHECKING", "LOADED", "FAILED", "ABORTED" or "UNKNOWN (UNKNOWN_STATUS)" (where "UNKNOWN_STATUS" will be a value from the {@link CB_AudioFileCache#status} property not recognized as any possible status).
2924 */
2925CB_AudioFileCache.prototype.getStatusString = function()
2926{
2927 var status = this.getStatus();
2928 var statuses = [ "UNLOADED", "LOADING", "UNCHECKED", "CHECKING", "LOADED", "FAILED", "ABORTED" ];
2929 if (typeof(statuses[status]) !== "undefined") { return statuses[status]; }
2930 else { return "UNKNOWN (" + status + ")"; }
2931}
2932
2933
2934/**
2935 * Calls the error function which should be set in the {@link CB_AudioFileCache#onError} property (if any), being "this" the {@link CB_AudioFileCache} object itself. Internal usage only recommended.
2936 * @function
2937 * @param {string} [message] - The message describing the error that will be sent to the set {@link CB_AudioFileCache#onError} function (if any) as the first and unique parameter.
2938 * @param {boolean} [avoidFailing=false] - If set to true, it will not set the {@link CB_AudioFileCache#status} property to "FAILED" (the value of the {@link CB_AudioFile#FAILED} property).
2939 * @returns {boolean} Returns true if the {@link CB_AudioFileCache#onError} function could be called or false otherwise.
2940 */
2941CB_AudioFileCache.prototype.errorFunction = function(message, avoidFailing)
2942{
2943 if (!avoidFailing) { this.status = CB_AudioFileCache.FAILED; }
2944 if (typeof(this.onError) === "function") { this.onError.call(this, message); return true; }
2945 return false;
2946}</pre>
2947 </article>
2948</section>
2949
2950
2951
2952
2953
2954 </div>
2955 </div>
2956
2957 <div class="clearfix"></div>
2958
2959
2960
2961</div>
2962</div>
2963
2964
2965 <div class="modal fade" id="searchResults">
2966 <div class="modal-dialog">
2967 <div class="modal-content">
2968 <div class="modal-header">
2969 <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
2970 <h4 class="modal-title">Search results</h4>
2971 </div>
2972 <div class="modal-body"></div>
2973 <div class="modal-footer">
2974 <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
2975 </div>
2976 </div><!-- /.modal-content -->
2977 </div><!-- /.modal-dialog -->
2978 </div>
2979
2980
2981<footer>
2982
2983
2984 <span class="copyright">
2985 <a href="printable/" target="_blank">See a more printer-friendly version</a><hr /><span style="color:#000000">© <address style="display:inline; font-style:normal;"><a href="https://crossbrowdy.com/" target="_blank">CrossBrowdy</a> API documentation</address> by <a href="https://joanalbamaldonado.com/" target="_blank">Joan Alba Maldonado</a> - <a href="https://creativecommons.org/licenses/by/4.0/" target="_blank">Creative Commons Attribution 4.0 International</a><br />DocStrap Copyright © 2012-2015 The contributors to the JSDoc3 and DocStrap projects.</span>
2986 </span>
2987
2988<span class="jsdoc-message">
2989 Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.5.5</a>
2990
2991 on Mon Feb 3rd 2020
2992
2993 using the <a href="https://github.com/docstrap/docstrap">DocStrap template</a>.
2994</span>
2995</footer>
2996
2997<script src="scripts/docstrap.lib.js"></script>
2998<script src="scripts/toc.js"></script>
2999
3000 <script type="text/javascript" src="scripts/fulltext-search-ui.js"></script>
3001
3002
3003<script>
3004$( function () {
3005 $( "[id*='$']" ).each( function () {
3006 var $this = $( this );
3007
3008 $this.attr( "id", $this.attr( "id" ).replace( "$", "__" ) );
3009 } );
3010
3011 $( ".tutorial-section pre, .readme-section pre, pre.prettyprint.source" ).each( function () {
3012 var $this = $( this );
3013
3014 var example = $this.find( "code" );
3015 exampleText = example.html();
3016 var lang = /{@lang (.*?)}/.exec( exampleText );
3017 if ( lang && lang[1] ) {
3018 exampleText = exampleText.replace( lang[0], "" );
3019 example.html( exampleText );
3020 lang = lang[1];
3021 } else {
3022 var langClassMatch = example.parent()[0].className.match(/lang\-(\S+)/);
3023 lang = langClassMatch ? langClassMatch[1] : "javascript";
3024 }
3025
3026 if ( lang ) {
3027
3028 $this
3029 .addClass( "sunlight-highlight-" + lang )
3030 .addClass( "linenums" )
3031 .html( example.html() );
3032
3033 }
3034 } );
3035
3036 Sunlight.highlightAll( {
3037 lineNumbers : true,
3038 showMenu : true,
3039 enableDoclinks : true
3040 } );
3041
3042 $.catchAnchorLinks( {
3043 navbarOffset: 10
3044 } );
3045 $( "#toc" ).toc( {
3046 anchorName : function ( i, heading, prefix ) {
3047 return $( heading ).attr( "id" ) || ( prefix + i );
3048 },
3049 selectors : "#toc-content h1,#toc-content h2,#toc-content h3,#toc-content h4",
3050 showAndHide : false,
3051 smoothScrolling: true
3052 } );
3053
3054 $( "#main span[id^='toc']" ).addClass( "toc-shim" );
3055 $( '.dropdown-toggle' ).dropdown();
3056
3057 $( "table" ).each( function () {
3058 var $this = $( this );
3059 $this.addClass('table');
3060 } );
3061
3062} );
3063</script>
3064
3065
3066
3067<!--Navigation and Symbol Display-->
3068
3069<script>
3070 $( function () {
3071 $( '#main' ).localScroll( {
3072 offset : { top : 60 } //offset by the height of your header (give or take a few px, see what works for you)
3073 } );
3074 $( "dt.name" ).each( function () {
3075 var $this = $( this ).find("h4");
3076 var icon = $( "<i/>" ).addClass( "icon-plus-sign" ).addClass( "pull-right" ).addClass( "icon-white" );
3077 var dt = $(this);
3078 var children = dt.next( "dd" );
3079
3080 dt.prepend( icon ).css( {cursor : "pointer"} );
3081 dt.addClass( "member-collapsed" ).addClass( "member" );
3082
3083
3084 children.hide();
3085
3086 dt.children().on( "click", function () {
3087 children = dt.next( "dd" );
3088 children.slideToggle( "fast", function () {
3089
3090 if ( children.is( ":visible" ) ) {
3091 icon.addClass( "icon-minus-sign" ).removeClass( "icon-plus-sign" ).removeClass( "icon-white" );
3092 dt.addClass( "member-open" ).animate( "member-collapsed" );
3093 } else {
3094 icon.addClass( "icon-plus-sign" ).removeClass( "icon-minus-sign" ).addClass( "icon-white" );
3095 dt.addClass( "member-collapsed" ).removeClass( "member-open" );
3096 }
3097 } );
3098 } );
3099
3100 } );
3101 } );
3102</script>
3103
3104
3105<!--Google Analytics-->
3106
3107
3108
3109 <script type="text/javascript">
3110 $(document).ready(function() {
3111 SearcherDisplay.init();
3112 });
3113 </script>
3114
3115
3116</body>
3117</html>