src/find.js

  1. /**
  2. * #Finding#
  3. * Methods for selecting & working with audio nodes
  4. */
  5. /**
  6. * Updates the internal selected nodes array with a collection of audio nodes matching the selector provided. Type, Class & Id selectors are supported.
  7. * <pre>
  8. * <code>//type selector using the node name, sets the frequency for all sines
  9. * __("sine").frequency(200);
  10. *
  11. * //set the frequency for the node with id "foo"
  12. * __("#foo").frequency(200);
  13. *
  14. * //set the frequency for any nodes with a class of "bar"
  15. * __(".bar").frequency(200);
  16. *
  17. * //select all sines, any nodes with classname "bar" or id of "foo"
  18. * //and set their frequencies to 200
  19. * __("sine,.bar,#foo").frequency(200);</code></pre>
  20. *
  21. * [See more selector examples](examples/selector.html)
  22. *
  23. * If invoked without arguments, cracked() resets the selection/connection state, removing any record of previous nodes and effectively marking the start of a new connection chain. Since a new node will try to connect to any previous node, calling __() tells a node that there is no previous node to connect to.
  24. * For example:
  25. * <pre>
  26. * <code>//Create & connect sine -> lowpass -> dac
  27. * __().sine();
  28. * __.lowpass();
  29. * __.dac();
  30. *
  31. * //Create but don't connect
  32. * __().sine();
  33. * __().lowpass();
  34. * __().dac();</code></pre>
  35. *
  36. * cracked is also the namespace for public methods and also can be written as a
  37. * double underscore __
  38. * <pre>
  39. * <code>__("sine"); //same as cracked("sine")</code>
  40. * </pre>
  41. *
  42. *
  43. * @public
  44. * @category Find
  45. * @type cracked
  46. * @function
  47. * @namespace
  48. * @global
  49. * @param {String} [selector] selector expression
  50. * @returns {cracked}
  51. */
  52. var cracked = find;
  53. function find() {
  54. if (arguments && arguments.length) {
  55. if (recordingMacro()) {
  56. //if we're making a macro right now
  57. //search in the macro
  58. findInMacro(arguments[0]);
  59. } else {
  60. //search everywhere
  61. if(__.isStr(arguments[0])) {
  62. var selector = arguments[0];
  63. _currentSelector = selector;
  64. _selectedNodes = getNodesWithSelector(selector);
  65. } else if(__.isObj(arguments[0]) && arguments[0].constructor.name === "AudioNode") {
  66. _currentSelector = arguments[0].getType();
  67. _selectedNodes = [arguments[0].getUUID()];
  68. }
  69. }
  70. } else {
  71. //if there are no arguments
  72. //then reset the entire state
  73. reset();
  74. }
  75. //if we're finding, then no previous node
  76. _previousNode = null;
  77. return cracked;
  78. }
  79. /**
  80. * find nodes in a macro with a selector updates the _selectedNodes array
  81. * @function
  82. * @private
  83. */
  84. function findInMacro() {
  85. if (arguments && arguments.length) {
  86. if(__.isStr(arguments[0])) {
  87. var macroUUID = getCurrentMacro().getUUID();
  88. //update the shared _currentSelector variable
  89. //then find the nodes
  90. _currentSelector = processSelectorForMacro(arguments[0]);
  91. //update selectedNodes
  92. _selectedNodes = getNodesWithSelector(_currentSelector);
  93. //strip out anything we found that's not part of this
  94. //container macro
  95. _selectedNodes.forEach(function (el, i, arr) {
  96. if (el && getNodeWithUUID(el).getMacroContainerUUID() !== macroUUID) {
  97. arr.splice(i, 1);
  98. }
  99. });
  100. } else if(__.isObj(arguments[0]) && arguments[0].constructor.name === "AudioNode") {
  101. _currentSelector = getCurrentMacroNamespace()+" "+arguments[0].getType();
  102. _selectedNodes = [arguments[0].getUUID()];
  103. }
  104. }
  105. }
  106. //helper method used above and in cracked.find()
  107. //prepends macro name to incoming selectors
  108. function processSelectorForMacro(selector) {
  109. //look for the macro namespace in the incoming selector
  110. //if its there, do nothing, else add it.
  111. var selectorArr = selector.split(","),
  112. prefix = getCurrentMacroNamespace();
  113. //insert the prefix
  114. //use a loop to handle comma delimited selectors
  115. for (var i = 0; i < selectorArr.length; i++) {
  116. selectorArr[i] = (selectorArr[i].indexOf(prefix) !== -1) ?
  117. selectorArr[i] : prefix + selectorArr[i];
  118. }
  119. //re-join the now prefixed selectors and return
  120. return selectorArr.join(",");
  121. }
  122. /**
  123. * reset state
  124. * @function
  125. * @private
  126. */
  127. function reset() {
  128. _previousNode = null;
  129. _selectedNodes = [];
  130. _currentSelector = "";
  131. }
  132. /**
  133. * reset selection
  134. * @function
  135. * @private
  136. */
  137. function resetSelection() {
  138. _selectedNodes = [];
  139. _currentSelector = "";
  140. }
  141. /**
  142. * resets everything to its initial state
  143. * <pre><code>//reset state for the entire app
  144. * cracked.reset();</code></pre>
  145. * @public
  146. * @category Find
  147. * @name cracked#reset
  148. * @memberof cracked
  149. * @function
  150. * @returns {cracked}
  151. */
  152. cracked.reset = function() {
  153. __("*").remove();
  154. resetMacro();
  155. reset();
  156. resetModel();
  157. resetLoop();
  158. return cracked;
  159. };
  160. /**
  161. * executes a method with a specific set of selected nodes without modifying the internal selectedNodes array
  162. * <pre><code>//filter everything but the sines from currently selected nodes and
  163. * //execute the frequency method against the remaining sines.
  164. * //the internal _selectedNodes array remains unchanged
  165. * cracked.exec(
  166. * "frequency",
  167. * 200,
  168. * cracked.filter("sine")
  169. * );</code></pre>
  170. *
  171. * @public
  172. * @category Find
  173. * @function
  174. * @name cracked#exec
  175. * @memberof cracked
  176. * @param {String} method method name
  177. * @param {Array} args arguments to supply to the method
  178. * @param {Array} nodes node array to execute against
  179. * @returns {cracked}
  180. */
  181. cracked.exec = function (method, args, nodes) {
  182. var save = _selectedNodes;
  183. _selectedNodes = nodes;
  184. cracked[method].apply(cracked, args);
  185. _selectedNodes = save;
  186. return cracked;
  187. };
  188. /**
  189. * iterate over the selectedNodes array, executing the supplied function for each element
  190. * <pre><code>__.each(type, function(node,index,array){
  191. * //Loops over any selected nodes. Parameters are the
  192. * //current node, current index, and the selectedNode array
  193. * });</code></pre>
  194. *
  195. * @public
  196. * @category Find
  197. * @name cracked#each
  198. * @memberof cracked
  199. * @function
  200. * @param {String} type string to be checked against the node type
  201. * @param {Function} fn function to be called on each node
  202. * @returns {cracked}
  203. */
  204. cracked.each = function (type, fn) {
  205. if (__.isFun(fn)) {
  206. for (var i = 0; i < _selectedNodes.length; i++) {
  207. var node = getNodeWithUUID(_selectedNodes[i]);
  208. if (!type || (type && node.getType() === type)) {
  209. fn(node, i, _selectedNodes);
  210. }
  211. }
  212. }
  213. return cracked;
  214. };
  215. /**
  216. * Filter selected nodes with an additional selector returns node array that can used with exec()
  217. * <pre><code>//select any sine & sawtooth oscillators
  218. * __("sine,saw");
  219. *
  220. * //filter out everything but the sines and
  221. * //execute the frequency method against those nodes.
  222. * //the internal _selectedNodes array remains unchanged
  223. * cracked.exec(
  224. * "frequency",
  225. * 200,
  226. * cracked.filter("sine")
  227. * );</code></pre>
  228. *
  229. * @public
  230. * @category Find
  231. * @name cracked#filter
  232. * @memberof cracked
  233. * @function
  234. * @param {String} selector selector expression
  235. * @returns {Array}
  236. */
  237. cracked.filter = function () {
  238. var tmp = [];
  239. if (arguments && arguments.length) {
  240. var str = arguments[0],
  241. selectorType = getSelectorType(str),
  242. match = str.match(/^\.|\#/) ? str.substring(1) : str;
  243. _selectedNodes.forEach(function (nodeID, i, arr) {
  244. var node = getNodeWithUUID(nodeID);
  245. if (
  246. selectorType === "type" && node.getType() === match ||
  247. selectorType === "class" && node.getClass() === match ||
  248. selectorType === "id" && node.getID() === match
  249. ) {
  250. tmp.push(nodeID);
  251. }
  252. });
  253. }
  254. return tmp;
  255. };
  256. /**
  257. * Find nodes with a selector returns node array that can used with exec()
  258. * <pre><code>//find all the sines in the patch and
  259. * //execute the frequency method against those nodes.
  260. * //the internal _selectedNodes array remains unchanged
  261. * cracked.exec(
  262. * "frequency",
  263. * 200,
  264. * cracked.find("sine")
  265. * );</code></pre>
  266. *
  267. * @public
  268. * @category Find
  269. * @name cracked#find
  270. * @memberof cracked
  271. * @function
  272. * @param {String} selector selector expression
  273. * @returns {Array}
  274. */
  275. cracked.find = function () {
  276. var selector = recordingMacro() ? processSelectorForMacro(arguments[0]) : arguments[0];
  277. return getNodesWithSelector(selector);
  278. };