Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
YUI.add('tree-sortable', function (Y, NAME) {
2
 
3
/*jshint expr:true, onevar:false */
4
 
5
/**
6
Extension for `Tree` that makes nodes sortable.
7
 
8
@module tree
9
@submodule tree-sortable
10
@main tree-sortable
11
**/
12
 
13
/**
14
Extension for `Tree` that makes nodes sortable.
15
 
16
@class Tree.Sortable
17
@constructor
18
@param {Object} [config] Configuration options.
19
@param {Function} [config.sortComparator] Default comparator function to use
20
    when sorting a node's children if the node itself doesn't have a custom
21
    comparator function. If not specified, insertion order will be used by
22
    default.
23
@param {Boolean} [config.sortReverse=false] If `true`, node children will be
24
    sorted in reverse (descending) order by default. Otherwise they'll be sorted
25
    in ascending order.
26
@extensionfor Tree
27
**/
28
 
29
/**
30
Fired after a node's children are re-sorted.
31
 
32
@event sort
33
@param {Tree.Node} node Node whose children were sorted.
34
@param {Boolean} reverse `true` if the children were sorted in reverse
35
    (descending) order, `false` otherwise.
36
@param {String} src Source of the event.
37
**/
38
var EVT_SORT = 'sort';
39
 
40
function Sortable() {}
41
 
42
Sortable.prototype = {
43
    // -- Public Properties ----------------------------------------------------
44
 
45
    /**
46
    If `true`, node children will be sorted in reverse (descending) order by
47
    default. Otherwise they'll be sorted in ascending order.
48
 
49
    @property {Boolean} sortReverse
50
    @default false
51
    **/
52
    sortReverse: false,
53
 
54
    // -- Lifecycle ------------------------------------------------------------
55
    initializer: function (config) {
56
        this.nodeExtensions = this.nodeExtensions.concat(Y.Tree.Node.Sortable);
57
 
58
        if (config) {
59
            if (config.sortComparator) {
60
                this.sortComparator = config.sortComparator;
61
            }
62
 
63
            if ('sortReverse' in config) {
64
                this.sortReverse = config.sortReverse;
65
            }
66
        }
67
    },
68
 
69
    // -- Public Methods -------------------------------------------------------
70
 
71
    /**
72
    Sorts the children of every node in this tree.
73
 
74
    A `sort` event will be fired for each node whose children are sorted, which
75
    can get very noisy. If this is a large tree, you may want to set the
76
    `silent` option to `true` to suppress these events.
77
 
78
    @method sort
79
    @param {Object} [options] Options.
80
        @param {Boolean} [options.silent] If `true`, no `sort` events will be
81
            fired.
82
        @param {Function} [options.sortComparator] Custom comparator function to
83
            use. If specified, this will become the new comparator function for
84
            each node, overwriting any previous comparator function that was set
85
            for the node.
86
        @param {Boolean} [options.sortReverse] If `true`, children will be
87
            sorted in reverse (descending) order. Otherwise they'll be sorted in
88
            ascending order. This will become each node's new sort order,
89
            overwriting any previous sort order that was set for the node.
90
        @param {String} [options.src] Source of the sort operation. Will be
91
            passed along to the `sort` event facade.
92
    @chainable
93
    **/
94
    sort: function (options) {
95
        return this.sortNode(this.rootNode, Y.merge(options, {deep: true}));
96
    },
97
 
98
    /**
99
    Default comparator function to use when sorting a node's children if the
100
    node itself doesn't have a custom comparator function.
101
 
102
    If not specified, insertion order will be used by default.
103
 
104
    @method sortComparator
105
    @param {Tree.Node} node Node being sorted.
106
    @return {Number|String} Value by which the node should be sorted relative to
107
        its siblings.
108
    **/
109
    sortComparator: function (node) {
110
        return node.index();
111
    },
112
 
113
    /**
114
    Sorts the children of the specified node.
115
 
116
    By default, only the node's direct children are sorted. To sort all nodes in
117
    the hierarchy (children, children's children, etc.), set the `deep` option
118
    to `true`. If this is a very deep hierarchy, you may also want to set
119
    `silent` to true to avoid generating a flood of `sort` events.
120
 
121
    @method sortNode
122
    @param {Tree.Node} node Node whose children should be sorted.
123
    @param {Object} [options] Options.
124
        @param {Boolean} [options.deep=false] If `true`, all of this node's
125
            children (and their children, and so on) will be traversed and
126
            re-sorted as well.
127
        @param {Boolean} [options.silent] If `true`, no `sort` event will be
128
            fired.
129
        @param {Function} [options.sortComparator] Custom comparator function to
130
            use. If specified, this will become the node's new comparator
131
            function, overwriting any previous comparator function that was set
132
            for the node.
133
        @param {Boolean} [options.sortReverse] If `true`, children will be
134
            sorted in reverse (descending) order. Otherwise they'll be sorted in
135
            ascending order. This will become the node's new sort order,
136
            overwriting any previous sort order that was set for the node.
137
        @param {String} [options.src] Source of the sort operation. Will be
138
            passed along to the `sort` event facade.
139
    @chainable
140
    **/
141
    sortNode: function (node, options) {
142
        // Nothing to do if the node has no children.
143
        if (!node.children.length) {
144
            return this;
145
        }
146
 
147
        options || (options = {});
148
 
149
        if (options.deep) {
150
            // Unset the `deep` option so we don't cause an infinite loop.
151
            options = Y.merge(options, {deep: false});
152
 
153
            var self = this;
154
 
155
            // Traverse and sort all nodes (including this one).
156
            this.traverseNode(node, function (nodeToSort) {
157
                self.sortNode(nodeToSort, options);
158
            });
159
 
160
            return this;
161
        }
162
 
163
        var comparator = this._getSortComparator(node, options),
164
            reverse;
165
 
166
        if ('sortReverse' in options) {
167
            reverse = node.sortReverse = options.sortReverse;
168
        } else if ('sortReverse' in node) {
169
            reverse = node.sortReverse;
170
        } else {
171
            reverse = this.sortReverse;
172
        }
173
 
174
        node.children.sort(Y.rbind(this._sort, this, comparator, reverse));
175
        node._isIndexStale = true;
176
 
177
        if (!options.silent) {
178
            this.fire(EVT_SORT, {
179
                node   : node,
180
                reverse: !!reverse,
181
                src    : options.src
182
            });
183
        }
184
 
185
        return this;
186
    },
187
 
188
    // -- Protected Methods ----------------------------------------------------
189
 
190
    /**
191
    Compares value _a_ to value _b_ for sorting purposes.
192
 
193
    Values are assumed to be the result of calling a sortComparator function.
194
 
195
    @method _compare
196
    @param {Mixed} a First value to compare.
197
    @param {Mixed} b Second value to compare.
198
    @return {Number} `-1` if _a_ should come before _b_, `0` if they're
199
        equivalent, `1` if _a_ should come after _b_.
200
    @protected
201
    **/
202
    _compare: function (a, b) {
203
        return a < b ? -1 : (a > b ? 1 : 0);
204
    },
205
 
206
    /**
207
    Compares value _a_ to value _b_ for sorting purposes, but sorts them in
208
    reverse (descending) order.
209
 
210
    Values are assumed to be the result of calling a sortComparator function.
211
 
212
    @method _compareReverse
213
    @param {Mixed} a First value to compare.
214
    @param {Mixed} b Second value to compare.
215
    @return {Number} `-1` if _a_ should come before _b_, `0` if they're
216
        equivalent, `1` if _a_ should come after _b_.
217
    @protected
218
    **/
219
    _compareReverse: function (a, b) {
220
        return b < a ? -1 : (b > a ? 1 : 0);
221
    },
222
 
223
    /**
224
    Overrides `Tree#_getDefaultNodeIndex()` to provide insertion-time sorting
225
    for nodes inserted without an explicit index.
226
 
227
    @method _getDefaultNodeIndex
228
    @param {Tree.Node} parent Parent node.
229
    @param {Tree.Node} node Node being inserted.
230
    @param {Object} [options] Options passed to `insertNode()`.
231
    @return {Number} Index at which _node_ should be inserted into _parent_'s
232
        `children` array.
233
    @protected
234
    **/
235
    _getDefaultNodeIndex: function (parent, node) {
236
        /*jshint bitwise:false */
237
 
238
        var children   = parent.children,
239
            comparator = this._getSortComparator(parent),
240
            max        = children.length,
241
            min        = 0,
242
            reverse    = 'sortReverse' in parent ? parent.sortReverse : this.sortReverse;
243
 
244
        if (!max) {
245
            return max;
246
        }
247
 
248
        // Special case: if the sortComparator is the default sortComparator,
249
        // cheat and just return the first or last index of the children array.
250
        //
251
        // This is necessary because the default sortComparator relies on
252
        // the node's index, which is always -1 for uninserted nodes.
253
        if (comparator._unboundComparator === Sortable.prototype.sortComparator) {
254
            return reverse ? 0 : max;
255
        }
256
 
257
        var compare = reverse ? this._compareReverse : this._compare,
258
            needle  = comparator(node);
259
 
260
        // Perform an iterative binary search to determine the correct position
261
        // for the node based on the return value of the comparator function.
262
        var middle;
263
 
264
        while (min < max) {
265
            middle = (min + max) >> 1; // Divide by two and discard remainder.
266
 
267
            if (compare(comparator(children[middle]), needle) < 0) {
268
                min = middle + 1;
269
            } else {
270
                max = middle;
271
            }
272
        }
273
 
274
        return min;
275
    },
276
 
277
    /**
278
    Returns a sort comparator function derived from the given _node_ and
279
    _options_, and bound to the correct `thisObj` based on where it was found.
280
 
281
    @method _getSortComparator
282
    @param {Tree.Node} node Node on which to look for a `sortComparator`
283
        function.
284
    @param {Object} [options] Options object on which to look for a
285
        `sortComparator` function.
286
    @return {Function} Properly bound sort comparator function.
287
    @protected
288
    **/
289
    _getSortComparator: function (node, options) {
290
        var boundComparator,
291
            comparator,
292
            thisObj;
293
 
294
        if (options && options.sortComparator) {
295
            comparator = node.sortComparator = options.sortComparator;
296
        } else if (node.sortComparator) {
297
            comparator = node.sortComparator;
298
            thisObj    = node;
299
        } else {
300
            comparator = this.sortComparator;
301
            thisObj    = this;
302
        }
303
 
304
        boundComparator = function () {
305
            return comparator.apply(thisObj, arguments);
306
        };
307
 
308
        boundComparator._unboundComparator = comparator;
309
 
310
        return boundComparator;
311
    },
312
 
313
    /**
314
    Array sort function used by `sortNode()` to re-sort a node's children.
315
 
316
    @method _sort
317
    @param {Tree.Node} a First node to compare.
318
    @param {Tree.Node} b Second node to compare.
319
    @param {Function} comparator Comparator function.
320
    @param {Boolean} [reverse=false] If `true`, this will be a reverse
321
        (descending) comparison.
322
    @return {Number} `-1` if _a_ is less than _b_, `0` if equal, `1` if greater.
323
    @protected
324
    **/
325
    _sort: function (a, b, comparator, reverse) {
326
        return this[reverse ? '_compareReverse' : '_compare'](
327
            comparator(a), comparator(b));
328
    }
329
};
330
 
331
Y.Tree.Sortable = Sortable;
332
/**
333
@module tree
334
@submodule tree-sortable
335
**/
336
 
337
/**
338
`Tree.Node` extension that adds methods useful for nodes in trees that use the
339
`Tree.Sortable` extension.
340
 
341
@class Tree.Node.Sortable
342
@constructor
343
@extensionfor Tree.Node
344
**/
345
 
346
function NodeSortable() {}
347
 
348
NodeSortable.prototype = {
349
    /**
350
    Sorts this node's children.
351
 
352
    @method sort
353
    @param {Object} [options] Options.
354
        @param {Boolean} [options.silent] If `true`, no `sort` event will be
355
            fired.
356
        @param {Function} [options.sortComparator] Custom comparator function to
357
            use. If specified, this will become the node's new comparator
358
            function, overwriting any previous comparator function that was set
359
            for the node.
360
        @param {Boolean} [options.sortReverse] If `true`, children will be
361
            sorted in reverse (descending) order. Otherwise they'll be sorted in
362
            ascending order. This will become the node's new sort order,
363
            overwriting any previous sort order that was set for the node.
364
        @param {String} [options.src] Source of the sort operation. Will be
365
            passed along to the `sort` event facade.
366
    @chainable
367
    **/
368
    sort: function (options) {
369
        this.tree.sortNode(this, options);
370
        return this;
371
    }
372
};
373
 
374
Y.Tree.Node.Sortable = NodeSortable;
375
 
376
 
377
}, '3.18.1', {"requires": ["tree"]});