class Sqimitive.Ordered
Modifiers: protected
Holds positional information about objects contained within this instance.
Types | Notes | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
array of object | Keys:
|
_ordered is a properly sorted array of _children, in order determined by _sorter(). It’s kept in sync with _children automatically.
The format of _ordered objects conveniently matches that accepted by nestEx():
// Assuming both collections are Ordered and _owning, removes
// the first child from col1 and inserts it in col2, preserving
// the same position and key:
col2.nestEx(col1.at(0))
You’re advised against accessing _ordered at all. Instead, use at().
Defined in: sqimitive.js, line 5282 • Show code
_ordered: [],
Modifiers: static
Part of: tag_Utilities
Determines insertion position for a new member value
in array
using a binary search.
indexFor() is an adaptation of Underscore’s un:sortedIndex().
It supports two sort callback styles: relative ((a, b)
, as for
Array
’s sort()
) and weight-based ((a)
, as for
un:sortBy()).
First time func
is called it’s given only 1 argument (value
) and
expected to return value
’s weight (which is stored for later calls),
then it’s called repeatedly to compare value
against other members
(returning < 0 if b
must go before a
, > 0 if after, == 0 if their
sort order is the same and either can go in front of another).
In Ordered, indexFor() is used to maintain the order of children
(in _ordered) using _sorter() for func
. Usually there’s no need
to call indexFor() directly.
cx
is func
’s context (required).
func
ignores the first call, otherwise is the same as with
standard sort()
:
Sqimitive.Ordered.indexFor(a, v, function (a, b) {
return a > b ? +1 : (a < b ? -1 : 0)
// This would fail without a check because b may be null:
return b && (a.prop - b.prop)
})
Weight-based func
uses all 3 arguments:
Sqimitive.Ordered.indexFor(a, v, function (a, b, posB) {
var posA = a.someWeightProp // assuming it's a number.
return arguments.length == 1 ? posA : (posA - posB)
})
Defined in: sqimitive.js, lines 5603-5614 (12 lines) • Show code
indexFor: function (array, value, func, cx) {
var pos = array.length && func.call(cx, value)
for (var low = 0, high = array.length, rel = 1; low < high; rel) {
var mid = Math.floor((low + high) / 2)
rel = -func.call(cx, array[mid], value, pos)
// Exiting immediately if rel == 0 (found a member with the same sort
// order) - low remains next to current index so order of ? : is
// important.
rel < 0 ? (high = mid) : (low = mid + 1)
}
return low
},
},
Modifiers: protected
Called when a child’s position has changed or was assumed for the first time.
Name | Types | Notes |
---|---|---|
sqim | object | The child that has changed position. |
index | int | sqim ’s current (new) index in this ; can be given to
at() |
Typically, you’d listen to/override this method to keep positions of
children on screen (in their parent’s Base.el) in sync with their
“logical” order (in this
).
_repos: function (child, index) {
index >= this.length - 1
? child.el.appendTo(this.el)
: child.el.insertBefore(this.el.children()[index])
},
_repos() is called for newly nest’ed _children and those that
have changed pos
. It’s not called for removed children. The default
implementation in Ordered does nothing.
resort() calls _repos() for all children (even if some didn’t
change positions – this is hard to determine). Here, note Array
’s
forEach()
implications: going in ascending order from child #0 to
last, and if _repos() mutates children then _repos() is not called
for some of them (so don’t mutate).
from plainstub
Defined in: sqimitive.js, line 5490
Modifiers: protected
The comparison function for determining desired child order.
Types | Notes |
---|---|
< 0 if a must go before b | |
> 0 if it must go after | |
0 if they can go in any order; it’s generally recommended to avoid 0 to ensure that re-sorting the same array doesn’t result in a different order of members |
You want to override _sorter() if you are not happy with the default
implementation. If you do, see indexFor() for the invocation format.
Default implementation compares using pos
(if given to nestEx() for
an object) or key
(if nesting using _defaultKey() then keys are
_cid’s).
order
option of a given child if no explicit pos
was provided for it when nesting:
'=_sorter': function (sup, a, b, posB) {
var posA = a.pos == null ? a.child.get('order') : a.pos
return arguments.length == 2 ? posA
: (posA > posB ? +1 : (posA < posB ? -1
// If properties match - sort stably by unique and constant _cid's.
: (a.child._cid - b.child._cid)))
},
a
and b
are objects in the _ordered format. During nestEx() one
of them may be missing from _ordered but present in _children;
during resort() both are present in _ordered. b
is null
on the
first iteration (see indexFor()).
Note: if consumers of this object are using pos
, make sure either they
supply correct types or you normalize them in _sorter: number 10
is > 2
but string '10'
is < '2'
.
Defined in: sqimitive.js, lines 5436-5441 (6 lines) • Show code
_sorter: function (a, b, posB) {
a = a.pos == null ? a.key : a.pos
return arguments.length == 1 ? a
// Not simply a - posB to work with non-numeric key/pos.
: (a > posB ? +1 : (a < posB ? -1 : 0))
},
Returns detailed info about a child by its index in this collection.
Types | Notes |
---|---|
object | Entry in the format of _ordered |
With at() you can obtain a child’s key or pos
. Use it from util
functions like each()
which provide you with an index in _ordered
(or in the result of slice()).
Warning: at() returns the object from _ordered verbatim – do not change it, or shallow-copy before you do.
Unlike slice(), at() does not accept negative index
.
sqim.at(0) //=> {child: Sqimitive, key: 'foo', pos: 3}
sqim.at(999) //=> undefined (if length <= 999)
sqim.at(-1) //=> always undefined
this.groupBy(function (sqim, i) { return this.at(i).pos })
//=> {pos1value: [child, ...], value2: [child, ...]}
// If clone is a non-_owning Ordered collection, populate it from orig:
orig.each(function (child, i) {
clone.nestEx(orig.at(i))
})
// Using nest() is possible but will discard pos and keys of children:
orig.each(function (child) {
clone.nest(child)
})
Defined in: sqimitive.js, lines 5555-5557 (3 lines) • Show code
at: function (index) {
return this._ordered[index]
},
Ordered extends the inherited nestEx
(typically Base.nestEx())
and handles new options
keys:
Name | Types | Notes |
---|---|---|
pos | any, optional | The caller may explicitly specify new child’s
position relative to other children; pos may be used by _sorter()
(in the default implementation it is). |
index | number | Set on return to the actual position of the new child
in _ordered; index can be given to at() |
oldIndex | number | Only set on return if options.changed is unset. |
If re-nesting the same child on the same key, Base’s nestEx() does
nothing but Ordered’s nestEx updates the child’s position in
_ordered if old and new options.pos
are different (i.e.
!_.isEqual()
) and calls _repos(). options.changed
remains
false
. Detect this situation by comparing index
and oldIndex
:
col.nestEx({child: sqim, pos: -123}) // new child, explicit pos
//=> {..., changed: true, previous: null, index: 0}
col.nestEx({child: sqim, pos: 9000}) // existing child, changed pos
//=> {..., changed: false, previous: sqim, index: 3, oldIndex: 0}
Defined in: sqimitive.js, line 5285
Re-sorts the entire collection from scratch.
Types | Notes |
---|---|
this |
Call resort() if an option that affects the sort order (_sorter) was changed or if sorting was temporary disabled.
Calls _repos() on every member after sorting _ordered.
Sqimitive.Base.extend({
mixIns: [Sqimitive.Ordered],
_opt: {invert: false},
events: {
'=_sorter': function (sup) {
var res = sup(this, arguments)
// Invert sort order based on the option's value:
return this.get('invert') ? -res : res
},
change_invert: 'resort-',
},
})
var hook = this.on('=_indexFor', Sqimitive.Core.stub)
try {
this.assignChildren(data)
} finally {
this.off(hook)
this.resort()
}
Defined in: sqimitive.js, lines 5480-5488 (9 lines) • Show code
resort: function () {
this._ordered.sort(function (a, b) {
// function _sorter(a, b, posB) - obtain that posB (simulating first
// iteration of indexFor()).
return this._sorter(a, b, this._sorter(b))
}.bind(this))
this.each(this._repos, this)
return this
},