1 var d3_raphael_selection = function(groups, d3_raphael_root) {
  2     d3_arraySubclass(groups, d3_raphael_selectionPrototype);
  3     groups.root = d3_raphael_root;
  4 
  5     return groups;
  6 };
  7 
  8 var d3_raphael_selectionPrototype = [];
  9 
 10 // todo: see if it is possible to generalize this method from the almost identical one in d3
 11 
 12 /**
 13  * Binds the provided data to the selected Raphael element(s). <br />
 14  * <br />
 15  * IMPLEMENTATION NOTE: Usage identical to the native d3 version, except internally, instead of binding the data to the DOM objects (like d3 does),
 16  * the data is bound to the Raphael wrapper element(s) of the DOM.
 17  *
 18  * @see <a href="https://github.com/mbostock/d3/wiki/Selections#wiki-data">d3.selection.data()</a>
 19  * @param value
 20  * @param {function} key_function
 21  * @return {D3RaphaelUpdateSelection}
 22  *
 23  * @function
 24  * @name D3RaphaelSelection#data
 25  */
 26 d3_raphael_selectionPrototype.data = function(value, key_function) {
 27     var i = -1,
 28         n = this.length,
 29         group,
 30         node;
 31 
 32     // If no value is specified, return the first value.
 33     if (!arguments.length) {
 34         value = new Array(n = (group = this[0]).length);
 35         while (++i < n) {
 36             if (node = group[i]) {
 37                 value[i] = node.__data__;
 38             }
 39         }
 40         return value;
 41     }
 42 
 43     function bind(group, groupData) {
 44         var i,
 45             n = group.length,
 46             m = groupData.length,
 47             n0 = Math.min(n, m),
 48             n1 = Math.max(n, m),
 49             updateNodes = [],
 50             enterNodes = [],
 51             exitNodes = [],
 52             node,
 53             nodeData;
 54 
 55         if (key_function) {
 56             var nodeByKeyValue = new d3_Map,
 57                 keyValues = [],
 58                 keyValue,
 59                 j = groupData.length;
 60 
 61             for (i = -1; ++i < n;) {
 62                 keyValue = key_function.call(node = group[i], node.__data__, i);
 63                 if (nodeByKeyValue.has(keyValue)) {
 64                     exitNodes[j++] = node; // duplicate key
 65                 } else {
 66                     nodeByKeyValue.set(keyValue, node);
 67                 }
 68                 keyValues.push(keyValue);
 69             }
 70 
 71             for (i = -1; ++i < m;) {
 72                 keyValue = key_function.call(groupData, nodeData = groupData[i], i)
 73                 if (nodeByKeyValue.has(keyValue)) {
 74                     updateNodes[i] = node = nodeByKeyValue.get(keyValue);
 75                     node.__data__ = nodeData;
 76                     enterNodes[i] = exitNodes[i] = null;
 77                 } else {
 78                     enterNodes[i] = d3_selection_dataNode(nodeData);
 79                     updateNodes[i] = exitNodes[i] = null;
 80                 }
 81                 nodeByKeyValue.remove(keyValue);
 82             }
 83 
 84             for (i = -1; ++i < n;) {
 85                 if (nodeByKeyValue.has(keyValues[i])) {
 86                     exitNodes[i] = group[i];
 87                 }
 88             }
 89         } else {
 90             for (i = -1; ++i < n0;) {
 91                 node = group[i];
 92                 nodeData = groupData[i];
 93                 if (node) {
 94                     node.__data__ = nodeData;
 95                     updateNodes[i] = node;
 96                     enterNodes[i] = exitNodes[i] = null;
 97                 } else {
 98                     enterNodes[i] = d3_selection_dataNode(nodeData);
 99                     updateNodes[i] = exitNodes[i] = null;
100                 }
101             }
102             for (; i < m; ++i) {
103                 enterNodes[i] = d3_selection_dataNode(groupData[i]);
104                 updateNodes[i] = exitNodes[i] = null;
105             }
106             for (; i < n1; ++i) {
107                 exitNodes[i] = group[i];
108                 enterNodes[i] = updateNodes[i] = null;
109             }
110         }
111 
112         enterNodes.update
113             = updateNodes;
114 
115         enterNodes.parentNode
116             = updateNodes.parentNode
117             = exitNodes.parentNode
118             = group.parentNode;
119 
120         enter.push(enterNodes);
121         update.push(updateNodes);
122         exit.push(exitNodes);
123     }
124 
125     var enter = d3_raphael_enterSelection([], this.root),
126         update = d3_raphael_selection([], this.root),
127         exit = d3_raphael_selection([], this.root);
128 
129     if (typeof value === "function") {
130         while (++i < n) {
131             bind(group = this[i], value.call(group, group.parentNode.__data__, i));
132         }
133     } else {
134         while (++i < n) {
135             bind(group = this[i], value);
136         }
137     }
138 
139     /**
140      * Returns the entering selection: placeholder nodes for each data element for which no corresponding existing DOM element was found in the current selection.
141      *
142      * @return {D3RaphaelEnterSelection}
143      *
144      * @see <code><a href="https://github.com/mbostock/d3/wiki/Selections#wiki-enter">d3.selection.enter()</a></code>
145      *
146      * @function
147      * @name D3RaphaelUpdateSelection#enter
148      */
149     update.enter = function() { return enter; };
150 
151     /**
152      * Returns the exiting selection: existing DOM elements in the current selection for which no new data element was found.
153      *
154      * @return {D3RaphaelSelection}
155      *
156      * @see <code><a href="https://github.com/mbostock/d3/wiki/Selections#wiki-exit">d3.selection.exit()</a></code>
157      *
158      * @function
159      * @name D3RaphaelUpdateSelection#exit
160      */
161     update.exit = function() { return exit; };
162     return update;
163 };
164 
165 /**
166  * Appends an element of the specified primitive type for each of the
167  * Raphael elements in the selection. <br />
168  * <br />
169  * NOTE: This method behaves similarly to the d3 version, except <strong>appended elements aren't children</strong>
170  * of the selection's existing elements.  In Raphael, all elements are in a flat list, peer to eachother, a child of
171  * the root containing element.
172  *
173  * @param {String} type
174  * @return {D3RaphaelSelection} with each existing element replaced with a appended element of the specified type.
175  *
176  * @see <a href="https://github.com/mbostock/d3/wiki/Selections#wiki-append">d3.selection.append()</a>
177  * @see d3_raphael_paperShapes for a list of supported primitive types
178  *
179  * @name D3RaphaelSelection#append
180  * @function
181  */
182 d3_raphael_selectionPrototype.append = function(type) {
183     var groups = [],
184         group,
185         nodeData;
186 
187     for(var j = 0; j < this.length; j++) {
188         groups.push((group = []));
189 
190         for(var i = 0; i < this[j].length; i++) {
191             if((nodeData = this[j][i])) {
192                 var newNode = this.root.create(type);
193 
194                 if("__data__" in nodeData)
195                     newNode.__data__ = nodeData.__data__;
196 
197                 group.push(newNode);
198             } else {
199                 group.push(null);
200             }
201         }
202     }
203 
204     return d3_raphael_selection(groups, this.root);
205 }
206 
207 /**
208  * Manipulates the Raphael elements in this selection by changing the specified attribute to
209  * the specified value.  <br/>
210  * <br/>
211  * Generally, it behaves similarly to the d3 version.  Like d3, the <code>value</code>
212  * parameter can be a function to provide an attribute value specific to each elements of the selection.<br />
213  * <br />
214  * In addition to the attributes supported natively by Raphael, there are few additions:
215  * <dl>
216  *     <dt>d</dt><dd>is an alias for Raphael attribute <code>path</code>. (Intended for compatibility with existing d3 code)</dd>
217  *     <dt>class</dt><dd>Sets the element's class name (like d3 does for the same attribute name)</dd>
218  * </dl>
219  *
220  * @param {String} name Raphael attribute name
221  * @param {value of function} value the value (or a function that returns the value) to change the attribute to
222  * @return {D3RaphaelSelection} this
223  *
224  * @see <a href="http://raphaeljs.com/reference.html#Element.attr">Raphael.element.attr()</a>
225  * @see <a href="https://github.com/mbostock/d3/wiki/Selections#wiki-attr">d3.selection.attr()</a>
226  *
227  * @function
228  * @name D3RaphaelSelection#attr
229  */
230 d3_raphael_selectionPrototype.attr = function(name, value) {
231     var valueF = (typeof value === "function") ? value : function() { return value; };
232     this.each(function() {
233         var value = valueF.apply(this, arguments);
234 
235         switch(name) {
236             case "class":
237                 this.addClass(value);
238                 break;
239             default:
240                 this.attr(name, value);
241         }
242 
243     });
244 
245     return this;
246 };
247 
248 /**
249  * Adds or removes the specified class name from the selections elements depending on the "truthness" of
250  * value. <br />
251  * <br />
252  * NOTE: Only adding class names is supported now, you cannot remove a class name currently with this method.
253  *
254  * @param {String} name class name
255  * @param {truthy or function} add
256  * @return {D3RaphaelSelection} this
257  *
258  * @see <a href="https://github.com/mbostock/d3/wiki/Selections#wiki-classed">d3.selection.classed()</a>
259  *
260  * @function
261  * @name D3RaphaelSelection#classed
262  */
263 d3_raphael_selectionPrototype.classed = function(name, add) {
264     var addF = d3_raphael_functify(add);
265 
266     this.each(function() {
267         if(addF.apply(this, arguments))
268             this.addClass(name);
269         else
270             throw_raphael_not_supported();
271     })
272 
273     return this;
274 }
275 
276 
277 /**
278  * Changes the text of the selection's <code>text</code> elements. <br />
279  * <br />
280  * NOTE: <strong>This version behaves differently than the native d3 version,</strong> which changes the text content of the
281  * selection's DOM elements.
282  *
283  * @see <a href="https://github.com/mbostock/d3/wiki/Selections#wiki-_text">d3.selection.text()</a>
284  *
285  * @param value
286  * @return {D3RaphaelSelection} this
287  *
288  * @function
289  * @name D3RaphaelSelection#text
290  */
291 d3_raphael_selectionPrototype.text = function(value) {
292     var valueF = d3_raphael_functify(value);
293 
294     this.each(function() {
295         this.attr("text", valueF.apply(this, arguments));
296     });
297 
298     return this;
299 }
300 
301 /**
302  * Performs a selection testing _all_ the elements in the Raphael paper that match the specified type, returning a new selection
303  * with only the first element found (if any). <br />
304  * <br />
305  * NOTE: <strong>This method behaves differently than the native d3 version.</strong>  Since the Raphael paper
306  * is inherently a flat list of elements, there is no concept of a selection that is scoped by it's parent element
307  * (like in d3).  Thus, every call to <code>select</code> searches on all elements in the paper, regardless of the
308  * existing content of the selection. <br />
309  * <br />
310  * NOTE: Currently, the selector string is limited in features.  Right now, you can only specify the Raphael primitive
311  * type name you want to select, no other selector strings are supported (like css class name).
312  *
313  * @see <a href="https://github.com/mbostock/d3/wiki/Selections#wiki-d3_select">d3.select()</a>
314  * @see d3_raphael_paperShapes for a list of supported primitive types
315  *
316  * @param {String} type Raphael primitive type name
317  * @return {D3RaphaelSelection} the new selection.
318  *
319  * @function
320  * @name D3RaphaelSelection#select
321  */
322 d3_raphael_selectionPrototype.select = function(type) {
323     return this.root.select(type);
324 };
325 
326 /**
327  * Performs a selection testing _all_ the elements in the Raphael paper that match the specified type, returning a new selection
328  * with the found elements (if any).<br />
329  * <br />
330  * NOTE: <strong>This method behaves differently than the native d3 version.</strong>  Since the Raphael paper
331  * is inherently a flat list of elements, there is no concept of a selection that is scoped by it's parent element
332  * (like in d3).  Thus, every call to <code>select</code> searches on all elements in the paper, regardless of the
333  * existing content of the selection. <br />
334  * <br />
335  * NOTE: Currently, the selector string is limited in features.  Right now, you can only specify the Raphael primitive
336  * type name you want to select, no other selector strings are supported (like css class name).
337  *
338  * @see <a href="https://github.com/mbostock/d3/wiki/Selections#wiki-d3_selectAll">d3.selectAll()</a>
339  * @see d3_raphael_paperShapes for a list of supported primitive types
340  *
341  * @param {String} type Raphael primitive type name
342  * @return {D3RaphaelSelection} the new selection.
343  *
344  * @function
345  * @name D3RaphaelSelection#selectAll
346  */
347 d3_raphael_selectionPrototype.selectAll = function(type) {
348     return this.root.selectAll(type);
349 };
350 
351 
352 /**
353  * Iterate over the elements of the selection, executing the specified function. <br />
354  * <br />
355  * NOTE: This method iterates over the Raphael created wrapper element (which internally contains the native DOM element,
356  * either SVG or VML via <code>element.node</code>.
357  *
358  * @see <a href="https://github.com/mbostock/d3/wiki/Selections#wiki-each">d3.selection.each()</a>
359  * @see <a href="http://raphaeljs.com/reference.html#Element">Raphael.Element</a>
360  *
361  * @param {function} callback <code>function(datum, index) { // this is the Raphael element }</code>
362  *
363  *  @function
364  *  @name D3RaphaelSelection#each
365  */
366 d3_raphael_selectionPrototype.each = d3_selectionPrototype.each;
367 
368 /**
369  * Returns true if the current selection is empty.
370  *
371  * @see <a href="https://github.com/mbostock/d3/wiki/Selections#wiki-empty">d3.selection.empty()</a>
372  *
373  * @function
374  * @name D3RaphaelSelection#empty
375  */
376 d3_raphael_selectionPrototype.empty = d3_selectionPrototype.empty;
377 
378 /**
379  * Returns the first non-null element in the current selection. If the selection is empty, returns null.
380  *
381  * @see <a href="https://github.com/mbostock/d3/wiki/Selections#wiki-node">d3.selection.node()</a>
382  *
383  * @function
384  * @name D3RaphaelSelection#node
385  */
386 d3_raphael_selectionPrototype.node = d3_selectionPrototype.node;
387 
388 /**
389  * Sets arbitrary properties on the selections Raphael elements.
390  *
391  * @see <a href="https://github.com/mbostock/d3/wiki/Selections#wiki-property">d3.selection.property()</a>
392  * @see <a href="http://raphaeljs.com/reference.html#Element">Raphael.Element</a>
393  *
394  * @param {String} name property name
395  * @param value property value
396  * @return {D3RaphaelSelection} this
397  *
398  * @function
399  * @name D3RaphaelSelection#property
400  */
401 d3_raphael_selectionPrototype.property = d3_selectionPrototype.property;
402 
403 /**
404  * Invokes the specified function once, passing in the current selection along with any optional arguments. The call operator always returns the current selection, regardless of the return value of the specified function.
405  *
406  * @see <a href="https://github.com/mbostock/d3/wiki/Selections#wiki-call">d3.selection.call()</a>
407  *
408  * @param {function} func
409  * @param {*} arguments optional arguments to pass to the function
410  *
411  * @function
412  * @name D3RaphaelSelection#call
413  */
414 d3_raphael_selectionPrototype.call = d3_selectionPrototype.call;
415 
416 
417 /**
418  * Gets or sets the bound data for each selection element.
419  *
420  * @see <a href="https://github.com/mbostock/d3/wiki/Selections#wiki-datum">d3.selection.datum()</a>
421  *
422  * @param {Array} value
423  * @return {D3RaphaelSelection} this
424  *
425  * @function
426  * @name D3RaphaelSelection#datum
427  */
428     d3_raphael_selectionPrototype.datum = d3_selectionPrototype.datum;
429 
430 d3_raphael_selectionPrototype.style = throw_raphael_not_supported;
431 d3_raphael_selectionPrototype.html = throw_raphael_not_supported;
432 d3_raphael_selectionPrototype.insert = throw_raphael_not_supported;
433 d3_raphael_selectionPrototype.filter = throw_raphael_not_supported;
434 d3_raphael_selectionPrototype.sort = throw_raphael_not_supported;
435 d3_raphael_selectionPrototype.order = throw_raphael_not_supported;
436 d3_raphael_selectionPrototype.on = throw_raphael_not_supported;
437 d3_raphael_selectionPrototype.transition = throw_raphael_not_supported;
438