Class in Sqimitive Base

class Sqimitive.Base extends Core
Implements what makes Sqimitive the Sqimitive – options (opt), children (chld) and filtering (util) on top of Core, which provides the f

Defined in: main.js, lines 4300-7345 (3046 lines)

Description (skip)

Implements what makes Sqimitive the Sqimitive – options (opt), children (chld) and filtering (util) on top of Core, which provides the fundamental event framework (evt).

If you work with DOM then look for Sqimitive\jQuery, which adds el and elEvents. If you want ordered children – use the Sqimitive\Ordered mixIn.

Example

It’s good practice to extend this class just once in your application and use the new base class everywhere else:

  • If Sqimitive’s class hierarchy or your needs change, you’d have to change just one reference to Base.
  • You’re likely to implement some application-specific application-wise logic sooner or later (such as logging) and it’s easily done if you have just one base class (of course, you should not ever change Sqimitive’s own prototypes).

var MyApp = {
  VERSION: '0.1',
}

MyApp.Sqimitive = Sqimitive.Base.extend()
// Or, if your application is DOM-based:
MyApp.Sqimitive = Sqimitive.jQuery.extend()

// Now use MyApp.Sqimitive as a base class throughout your code:
MyApp.ClassOne = MyApp.Sqimitive.extend(...)
MyApp.ClassTwo = MyApp.Sqimitive.extend(...)

Traditional OOP-style inheritance is not the only form of functionality extension supported by Sqimitive. For multi-parent inheritance, aka mix-ins, aka traits and for generics (parametrized mix-ins) see mixIn().

Properties

_childClass

Modifiers: protected

Part of: tag_Nesting

from settable

May be set upon declaration via Core::extend() or mixIn and/or read/written directly on run-time.

Ensures _children contains instances of a certain class only.

Value Types
Types Notes
Object to disable type checking Array and string are indirect declaration-time references (classref).
Sqimitive.Base the default
array
string
Example
var MyToDoItem = Sqimitive.Base.extend()
var SpecialMyToDoItem = MyToDoItem.extend()

var MyToDoList = Sqimitive.Base.extend({
  _childClass: MyToDoItem,
})

;(MyToDoList).nest(new MyToDoItem)         // works (the _childClass)
;(MyToDoList).nest({})                     // throws an exception
;(MyToDoList).nest(new Sqimitive.Base)     // throws an exception
;(MyToDoList).nest(new SpecialMyToDoItem)  // works (subclass of _childClass)
ExampleDisabling the check with Object:
var MyToDoList = Sqimitive.Base.extend({
  _childClass: Object,
})

;(MyToDoList).nest(new MyToDoItem)         // works
;(MyToDoList).nest({})                     // works
;(MyToDoList).nest(new Sqimitive.Base)     // works
;(MyToDoList).nest(new SpecialMyToDoItem)  // works

Other notes:

  • Typically, _childClass specifies a subclass of Sqimitive.Core (Core) since non-sqimitives are unlikely to work properly as children. This is not checked though.
  • _childClass constraint is enforced as long as children are added via nest() and other standard methods, not by direct manipulation of _children (which is highly discouraged anyway).
  • Changing _childClass on run-time affects only new nesting attempts (existing children are not validated).
  • _childClass is listed in _shareProps by default.

Indirect referencesclassref

It’s often convenient to provide _childClass as an array or string to extend() or mixIn(). In this case init() resolves the value of _childClass to the actual class, once per instantiation of every class (this makes it tad slower than providing an object value):

Members
Name Types Notes
arraylike [BaseObject, 'Sub.Class.Path'] Same as evaluating BaseClass.Sub.Class.Path
stringlike 'Sub.Class.Path' Relative to static properties of this
stringempty '' The class of this

init() errors if no class was found.

ExampleIndirect references are useful for “forward type declaration” where the child class is defined after the collection’s class or appears later on run-time:
var MyToDo = {}

MyToDo.List = Sqimitive.Base.extend({
  _childClass: [MyToDo, 'Item'],
})

MyToDo.Item = Sqimitive.Base.extend()
Or for the conventional Sqimitive hierarchy of <Collection>.<Child>:
MyToDo.List = Sqimitive.Base.extend({
  // All declarations below are equivalent:
  _childClass: [MyToDo, 'List.Item'],
  _childClass: [MyToDo.List, 'Item'],
  _childClass: 'Item',
})

MyToDo.List.Item = Sqimitive.Base.extend()

alert(MyToDo.List.prototype._childClass)                   //=> 'Item'
alert((new MyToDo.List)._childClass == MyToDo.List.Item)   //=> true

Defined in: main.js, line 4594Show code

_childClass: null,
_childEvents

Modifiers: protected

Part of: tag_Nesting

from settable

May be set upon declaration via Core::extend() or mixIn and/or read/written directly on run-time.

Lists event names for automatic _forward’ing from children to the collection object.

Value Types
Types Notes
array ['-nest_', 'change', ...]

Whenever a new child is nest()’ed, listens to these events on it, firing events on this with the same name but prefixed with a dot . (e.g. render → .render) and with the child’s object pushed in front of the event’s arguments. Think of this as of the usual ../../path notation in file systems where each dot means “one [parent] above”.

Nothing special is done to stop listening when a child is removed since the default unnested() implementation calls child.off(this) (see off).

Example
var MyList = Sqimitive.Base.extend({
  _childEvents: ['-change'],

  events: {
    '.-change': function (sqim, name) {
      alert('Option ' + name + ' is about to change on ' + sqim._cid)
    },
  },
})

Warning: don’t list unnest here – _parent will off() itself before that and never receive the notification. Use unnested instead. Using -unnest is also possible but in this case if an exception occurs during unnesting your handler won’t know this and will be called anyway, while the child is possibly left nested.

from inMergePropsA

This property is listed in _mergeProps by default so subclass defining it adds to its parent’s values instead of overwriting the array entirely.

Other notes:

ExampleUsing _childEvents is identical to manually calling on() but more convenient. However, since it’s on() in disguise, you can use any event prefixes (evtpf), specify methods that will be automatically turned into events (evt), etc.
var Collection = Sqimitive.Base.extend({
  _childEvents: ['=click'],

  _opt: {
    enabled: true,
  },

  events: {
    '.=click': function (child, sup) {
      if (this.get('enabled')) {
        return sup(this, arguments)
      } else {
        console.error('Clicking is disabled! Stop playing, ' + child._cid)
      }
    },

    '-unnested': function (child) {
      child.off(this)
    },
  },
})

var Item = Sqimitive.Base.extend({
  click: function () { alert('Oh... feels good!') },
})

var col = new Collection
var item = new Item
item.click()    // alerts; click is a method
col.nest(item)
  // item.click is no longer the original method but the wrapping handler
item.click()    // alerts
col.set('enabled', false)
item.click()    // no more alerts
col.unlist(item)
item.click()    // alerts again; click is again the original method
ExampleYou can forward already forwarded events with multi-level nesting (children of children of your collection) the same way. The number of dots indicates the number of child instances prepended to event’s arguments:
var MyListGroup = Sqimitive.Base.extend({
  // Indicate this object nests MyList instances from the first
  // example.
  _childClass: MyList,

  // MyList forwards '-change' on its children as '.-change' on itself
  // so we can foward that event too on this grouping instance.
  // There's no limit - '....-change' is perfectly fine and works on
  // 4th nesting level. Each forward gets originating object pushed in
  // front so '..-change' gets MyList as first argument. '...-change'
  // would get (MyListGroup, MyList).
  _childEvents: ['.-change'],
})

// Listening to '-change' that occurred on a MyList child, with MyList
// being nested into MyListGroup.
;(new MyListGroup).on('..-change', function (myListGroup, myList) { ... })

And of course you can use the usual event prefixes (evtpf) on these already-forwarded events:

// Trigger event on this before other handlers of '.-change'.
_childEvents: ['-.+normalize'],

// ...

;(new MyListGroup)
  .on('.-.+normalize_caption', function (myListGroup, myList,
                                         currentResult, newValue) {
    return newValue.trim()
  })

Defined in: main.js, line 4727Show code

_childEvents: [],
_children

Modifiers: protected

Part of: tag_Nesting

Holds references to objects contained within this instance (“collection”).

Value Types
Types Notes
object {key: Sqimitive} Keys are arbitrary strings as given to nest (_parentKey-s if this is _owning) and values are the children themselves (objects).

You’re advised against accessing _children at all. Instead, use nested() and other methods.

Other notes:

  • Both _owning sqimitives and not list their children here. For non-_owning, _children keys naturally differ from _parentKey of their children.
  • This parent-child relationship is purely formal and doesn’t dictate any DOM or other structure (children can have their el-s outside of the parent’s node). Moreover, if _owning is unset then it doesn’t imply the reverse relationship (from children to their parent).
  • See the children overview (chld) for examples.

Defined in: main.js, line 4457Show code

_children: {},
_initToOpt

Modifiers: protected

Part of: tag_Options

from setOnDecl

May only be set upon declaration via Core::extend() or mixIn.

Specifies _respToOpt rules for transforming opt given to init() (new) into this._opt.

_initToOpt is the assignResp()’s schema used by init(). Base’s value directly merges the opt object into own _opt, ignoring el. el by convention is meant to replace the declared class value, possibly mutated (see jQuery, for example).

Example
var My = Sqimitive.Base.extend({
  _initToOpt: {context: false},
  _context: null,

  events: {
    init: function (opt) {
      this._context = opt.context
    },
  },
})

// Or, shorter:
var My = Sqimitive.Base.extend({
  _initToOpt: {context: '._context'},
  _context: null,
})

var my = new My({context: cx})
  //=> my._opt = {}
  //=> my._context = cx
ExampleKeeping _opt clean by assigning only whitelisted keys:
var My = Sqimitive.Base.extend({
  _opt: {
    quux: 'initial',
  },

  // Thanks to _mergeProps, _initToOpt keys are merged with Base's.
  _initToOpt: {
    foo: true,        // set foo under _opt.foo
    bar: 'newBar',    // set bar under _opt.newBar
    // By default, assignResp() copies unlisted keys as is.
    '': function (opt, options) { options.onlyDefined = true },
  },
})

new My({quux: 0, foo: 1, bar: 2, baz: 3, el: 4})
  //=> _opt = {quux: 'initial', foo: 1, newBar: 2}
  //
  // 1. Keeps class' value for quux (because of onlyDefined)
  // 2. Takes foo
  // 3. Renames bar to newBar
  // 4. Ignores baz (because of onlyDefined)
  // 5. And el (because of {el: false} inherited from Base)

from rtobase

Value Types
Types Notes
object {respKey: optValue}.

_respToOpt’s keys are input object’s keys (except the special '') and values are one of the following (optValue):

Members
Name Types Notes
false Skip input item regardless of options.onlyDefined as given to assignResp().
true Assign input item’s value to the option named respKey (i.e. keys of the option and the input object are the same).
string Assign input item’s value to the option by this name. If begins with ., assign directly to a property on this (if sole . then assign to property named respKey) – typically used for public read-only properties.
function(respValue, key, resp, options) Input item transformation. This function is called in this context and must return [false|'optToSet|.[prop]', value] or falsy (equals [false]), with the first member treated as above.

respKey only determines the respValue given to this function; the latter can access the entire input object (resp). The (new) option’s name is retrieved from the returned array (optToSet), not from respKey.

The key argument equals respKey (i.e. is the key’s name under which the function is listed in _respToOpt), options is the object given to assignResp().

If optToSet is false then the input item is skipped and value is unused, otherwise it’s the option name to set value to (value can be of any type). It’s similar to calling set() but more declarative and future-proof.

The special key '' (empty string) must be a function (resp, options) returning object {optToSet: value} or null (equivalent to {}). It’s called in the beginning of assignResp() and, as other keys, if resp has an empty string key – check arguments.length if you expect it. This key is useful for unserializing fields that match multiple options or vice-versa:

Sqimitive.Base.extend({
  _respToOpt: {
    '': function (resp) {
      return {personsName: resp.firstName + ' ' + resp.secondName}
        // same as set('personsName', '<firstName> <secondName>')
    }

    // The opposite of the above - if there are two options in one
    // input key.
    '': function (resp) {
      var name = resp.personsName.split(' ')
      return {firstName: name[0], secondName: name[1]}
    }
  },
})

Example
Sqimitive.Base.extend({
  _respToOpt: {setAsIs: true, setAsFoo: 'Foo'},
})

// ...

sqim.assignResp({setAsIs: 123, setAsFoo: 'xyz'})
  // sqim._opt is {setAsIs: 123, Foo: 'xyz'}

// Equivalent to:
sqim.set('setAsIs', 123)
sqim.set('Foo', 'xyz')
ExampleUsing transformation functions:
Sqimitive.Base.extend({
  _respToOpt: {
    ignore: function () { return [false] },
    rename: function (value) { return ['foo', value] },
    date: function (value) { return ['date', new Date(value)] },
    merge: function (v, k, resp, options) {
      // v = 5, k = 'merge', resp = {unlisted: ...}, options = {}
      return ['bar', resp.a.concat(resp.b)
    },
    setUndefined: function () { return ['baz', undefined] },
  }
})

// ...

sqim.assignResp({unlisted: 1, ignore: 2, rename: 3, date: 4,
                 merge: 5, a: 6, b: 7, setUndefined: 8})
  // sqim._opt is {foo: 2, date: Date, bar: [6, 7], baz: undefined}

from unordered

Attention: JavaScript objects are unordered, as explained in the description of Ordered.

Keys are restored in arbitrary order except '' is always called first. The below example is buggy because object may be called before objects, when this.objects is not yet unserialized:

Sqimitive.Base.extend({
  objects: null,   // some kind of collection
  object: null,    // a member of objects

  _respToOpt: {
    objects: function (list) {
      this.objects.assignChildren(list)
    },
    object: function (key) {
      this.object = this.objects.nested(key)
    },
  },
})
objects must be restored from within '':
_respToOpt: {
  '': function (resp) {
    this.objects.assignChildren(resp.objects)
  },
  object: function (key) {
    // As before.
  },
},
This has to be done even if objects assignment is simple:
  // WRONG:
  _respToOpt: {
    objects: true,
    object: function (key) {
      this.object = this.objects[key]
    },
  },

  // WRONG:
  _respToOpt: {
    // No objects key = true if onlyDefined is false (as it is by default).

    object: function (key) {
      this.object = this.objects[key]
    },
  },

assignResp({objects: {k: ...}, object: 'k'}, {onlyDefined: false})

from inMergeProps

This property is listed in _mergeProps by default so subclass defining it adds to its parent’s object instead of overwriting it entirely (but identical keys are still overwritten).

Other notes:

  • Options are assigned with set() so normalization and change events take place as usual.
  • Missing keys may or may not become options by the same name – this depends on the options.onlyDefined flag of assignResp().
  • Leading . has no effect on respKey-s missing from schema: they are always either ignored (!onlyDefined) or set():
    sqim.assignResp({'.x': 1, '.y': 2}, {schema: {'.x': true}})
      //=> sqim.x is 1, sqim._opt['.y'] is 2
    sqim.assignResp({'.x': 1, '.y': 2}, {schema: {'.x': true}, onlyDefined: false})
      //=> sqim.x is 1, sqim._opt['.y'] is unchanged

Defined in: main.js, line 4964Show code

_initToOpt: {el: false},
_opt

Modifiers: protected

Part of: tag_Options

May be set upon declaration via Core::extend() or mixIn, read on run-time with get() and written with set().

List of “opt’ions” (public properties) of this instance.

Value Types
Types Notes
object {name: value} Keys are option names and values are anything, of arbitrary type.

On run-time, use get()/set() to access this data (both from within this class’ methods and from the outside – it’s a public interface).

When any option’s value is changed, ifSet:

  • Fires the normalize_OPT event to allow for value normalization and validation.
  • If no error occurred, changes the value in this._opt and fires change_OPT and change to notify the interested parties.
Example
var Parent = Sqimitive.Base.extend({
  _opt: {caption: 'unnamed'},

  events: {
    '+normalize_caption': function (res, s) {
      return s.trim()
    },

    change: function (optName, newValue) {
      alert(optName + '=' + newValue)
    },
  },
})

var Child = Parent.extend({
  _opt: {body: 'unbodied'},

  events: {
    change_caption: function (newValue) {
      alert('New caption = ' + newValue)
    },
  },
})

var child = new Child
  // child._opt = {caption: 'unnamed', body: 'unbodied'}

child.set('caption', 'Foo')
  // alerts: New caption = foo   - Child's change_caption handler
  // alerts: caption=foo         - Parent's change handler

child.set('body', 'Bar')
  // alerts: body=foo            - Parent's change handler

child.set('caption', ' S P A C E ')
  // _opt.caption  = 'S P A C E'
child.set('body',    ' S P A C E ')
  // _opt.body     = ' S P A C E '

When given to extend(), the _opt key specifies initial options (their defaults) for new instances.

from inMergeProps

This property is listed in _mergeProps by default so subclass defining it adds to its parent’s object instead of overwriting it entirely (but identical keys are still overwritten).

It’s highly advised to access _opt’s values only via get()/set() - performance benefits of direct access are questionable (especially when those methods are not events/firer-s, i.e. almost always) while lack of normalize_OPT and others often cause bugs.

from inBB

In Backbone…

In Backbone terms “options” are called bb:Model-attributes.

Defined in: main.js, line 4375Show code

_opt: {},
_owning

Modifiers: protected

Part of: tag_Nesting

from setOnDecl

May only be set upon declaration via Core::extend() or mixIn.

Specifies if this object manages its children or not (by default it does).

Value Types
Types Notes
true the default “Managing” (owning) parent. All of its children know who owns them (_parent) and under which key (_parentKey). It makes sure the children only ever have one parent – itself, and that they cannot duplicate in its _children. Essentially makes a bi-directional tree.
false Unmanaged (non-owning) parent. Acts as a simple collection. Imposes no hierarchy onto its children, who do not even know that they are listed here and may duplicate in this own _children (same child under different keys).

_owning only affects a subset of features (nest(), etc.). Most features – filtering (util), _forward’ing _childEvents, etc. can be used in both modes.

See chld children overview for more details.

Example
var Owning = Sqimitive.Base.extend()

var NonOwning = Sqimitive.Base.extend({
  _owning: false,
})

var child = new Sqimitive.Base
var owning = new Owning
var nonOwning = new NonOwning
owning.nest('key', child)
nonOwning.nest('key2', child)
alert(child._parent == owning)             //=> true
alert(child._parentKey)                    //=> 'key'
alert(owning.nested('key') == child)       //=> true
alert(nonOwning.nested('key'))             //=> undefined
alert(nonOwning.nested('key2') == child)   //=> true

var owning2 = new Owning
owning2.nest('key3', child)
  // child._parent == owning2, _parentKey == 'key3'
alert(owning.nested('key'))                //=> undefined

Defined in: main.js, line 4506Show code

_owning: true,
_parent

Modifiers: protected

Part of: tag_Nesting

from readOnly

May only be read, not changed.

Holds the reference to the Sqimitive that owns object, or null.

You can read this property from inside methods of the same class. Changing it (from any context) is highly discouraged because it’s easy to break object integrity – use nest(), unnest() and others.

parentAndKeyNon-_owning sqimitives never change _parent and _parentKey of their _children.

Example
Sqimitive.Base.extends({
  events: {
    owned: function () {
      // CORRECT: reading _parent from within this class' context:
      alert('New parent is ' + this._parent._cid)
      alert('My key under my parent is ' + this._parentKey)
      // WRONG: do not change _parent:
      this._parent = null
    },
  },
})

// WRONG: do not access _parent from the outside:
alert(sqim._parent._cid)

Defined in: main.js, line 4408Show code

_parent: null,
_parentKey

Modifiers: protected

Part of: tag_Nesting

from readOnly

May only be read, not changed.

Holds the key under which this instance is listed in its _parent’s list of _children, or null.

Value Types
Types Notes
string if owned If null then _parent is also null
null if not

from parentAndKey

Non-_owning sqimitives never change _parent and _parentKey of their _children.

Example
Sqimitive.Base.extends({
  events: {
    owned: function () {
      // CORRECT: reading _parent from within this class' context:
      alert('New parent is ' + this._parent._cid)
      alert('My key under my parent is ' + this._parentKey)
      // WRONG: do not change _parent:
      this._parent = null
    },
  },
})

// WRONG: do not access _parent from the outside:
alert(sqim._parent._cid)
ExampleThis key can be given to nested() and others:
Sqimitive.Base.extends({
  pull: function () {
    this._parent.unlist(this._parentKey)
      // this is a contrived example since this.remove() does
      // exactly the same
  },
})

Defined in: main.js, line 4432Show code

_parentKey: null,
_respToOpt

Modifiers: protected

Part of: tag_Options

from settable

May be set upon declaration via Core::extend() or mixIn and/or read/written directly on run-time.

Specifies rules for transforming an external input object (e.g. an API response) into _opt’ions used by assignResp().

See also _initToOpt that is used to assign opt given to init() (new) to this._opt.

Value Types
Types Notes
rtobaseobject {respKey: optValue}.

_respToOpt’s keys are input object’s keys (except the special '') and values are one of the following (optValue):

Members
Name Types Notes
false Skip input item regardless of options.onlyDefined as given to assignResp().
true Assign input item’s value to the option named respKey (i.e. keys of the option and the input object are the same).
string Assign input item’s value to the option by this name. If begins with ., assign directly to a property on this (if sole . then assign to property named respKey) – typically used for public read-only properties.
function(respValue, key, resp, options) Input item transformation. This function is called in this context and must return [false|'optToSet|.[prop]', value] or falsy (equals [false]), with the first member treated as above.

respKey only determines the respValue given to this function; the latter can access the entire input object (resp). The (new) option’s name is retrieved from the returned array (optToSet), not from respKey.

The key argument equals respKey (i.e. is the key’s name under which the function is listed in _respToOpt), options is the object given to assignResp().

If optToSet is false then the input item is skipped and value is unused, otherwise it’s the option name to set value to (value can be of any type). It’s similar to calling set() but more declarative and future-proof.

The special key '' (empty string) must be a function (resp, options) returning object {optToSet: value} or null (equivalent to {}). It’s called in the beginning of assignResp() and, as other keys, if resp has an empty string key – check arguments.length if you expect it. This key is useful for unserializing fields that match multiple options or vice-versa:

Sqimitive.Base.extend({
  _respToOpt: {
    '': function (resp) {
      return {personsName: resp.firstName + ' ' + resp.secondName}
        // same as set('personsName', '<firstName> <secondName>')
    }

    // The opposite of the above - if there are two options in one
    // input key.
    '': function (resp) {
      var name = resp.personsName.split(' ')
      return {firstName: name[0], secondName: name[1]}
    }
  },
})

Example
Sqimitive.Base.extend({
  _respToOpt: {setAsIs: true, setAsFoo: 'Foo'},
})

// ...

sqim.assignResp({setAsIs: 123, setAsFoo: 'xyz'})
  // sqim._opt is {setAsIs: 123, Foo: 'xyz'}

// Equivalent to:
sqim.set('setAsIs', 123)
sqim.set('Foo', 'xyz')
ExampleUsing transformation functions:
Sqimitive.Base.extend({
  _respToOpt: {
    ignore: function () { return [false] },
    rename: function (value) { return ['foo', value] },
    date: function (value) { return ['date', new Date(value)] },
    merge: function (v, k, resp, options) {
      // v = 5, k = 'merge', resp = {unlisted: ...}, options = {}
      return ['bar', resp.a.concat(resp.b)
    },
    setUndefined: function () { return ['baz', undefined] },
  }
})

// ...

sqim.assignResp({unlisted: 1, ignore: 2, rename: 3, date: 4,
                 merge: 5, a: 6, b: 7, setUndefined: 8})
  // sqim._opt is {foo: 2, date: Date, bar: [6, 7], baz: undefined}

from unordered

Attention: JavaScript objects are unordered, as explained in the description of Ordered.

Keys are restored in arbitrary order except '' is always called first. The below example is buggy because object may be called before objects, when this.objects is not yet unserialized:

Sqimitive.Base.extend({
  objects: null,   // some kind of collection
  object: null,    // a member of objects

  _respToOpt: {
    objects: function (list) {
      this.objects.assignChildren(list)
    },
    object: function (key) {
      this.object = this.objects.nested(key)
    },
  },
})
objects must be restored from within '':
_respToOpt: {
  '': function (resp) {
    this.objects.assignChildren(resp.objects)
  },
  object: function (key) {
    // As before.
  },
},
This has to be done even if objects assignment is simple:
  // WRONG:
  _respToOpt: {
    objects: true,
    object: function (key) {
      this.object = this.objects[key]
    },
  },

  // WRONG:
  _respToOpt: {
    // No objects key = true if onlyDefined is false (as it is by default).

    object: function (key) {
      this.object = this.objects[key]
    },
  },

assignResp({objects: {k: ...}, object: 'k'}, {onlyDefined: false})

from inMergeProps

This property is listed in _mergeProps by default so subclass defining it adds to its parent’s object instead of overwriting it entirely (but identical keys are still overwritten).

Other notes:

  • Options are assigned with set() so normalization and change events take place as usual.
  • Missing keys may or may not become options by the same name – this depends on the options.onlyDefined flag of assignResp().
  • Leading . has no effect on respKey-s missing from schema: they are always either ignored (!onlyDefined) or set():
    sqim.assignResp({'.x': 1, '.y': 2}, {schema: {'.x': true}})
      //=> sqim.x is 1, sqim._opt['.y'] is 2
    sqim.assignResp({'.x': 1, '.y': 2}, {schema: {'.x': true}, onlyDefined: false})
      //=> sqim.x is 1, sqim._opt['.y'] is unchanged

Defined in: main.js, line 4899Show code

_respToOpt: {},
el

Part of: tag_Lifecycle

from settable

May be set upon declaration via Core::extend() or mixIn and/or read/written directly on run-time.

“Element” – an external “form” of this Sqimitive (such as a DOM node).

Even though this is technically a public read/write property, it’s usually best not to change it on run-time (after initialization) or at least not from the outside context.

elstubSqimitive\Base doesn’t define any element-related functionality but only provides stubs for several common fields (el, render(), remove(), etc.) for standardized extension. See Sqimitive\jQuery.el for one such subclass.

Defined in: main.js, line 4983Show code

el: false,
elEvents

Part of: tag_Lifecycle

from setOnDecl

May only be set upon declaration via Core::extend() or mixIn.

Declares event listeners for el, bound automatically by attach().

Value Types
Types Notes
object {event: func}

elEvents’ format is consistent among Base’s subclasses and follows Backbone’s bb:Model-events: keys are event references of form event[.ns][ .sel #ector] and values are strings (expandFunc()) or functions, called as function (nativeEventObject) (see argDanger).

from inMergeProps

This property is listed in _mergeProps by default so subclass defining it adds to its parent’s object instead of overwriting it entirely (but identical keys are still overwritten).

The .ns part is ignored but can be used to create unique keys for the purpose of inheritance. By convention, the class’ own handlers don’t have ns while handlers of mixIn-s do.

from es6thiswarn

Warning: avoid using ES6 arrow functions as handlers due to their fixed this (es6this).

This property is not advised to change on run-time since it’s usually unpredictable when it will take effect. For example, Sqimitive\jQuery.attach() only binds events when nesting el into a new parent node:

sqim.attach()
sqim.elEvents['click'] = function () { alert('foo') }
sqim.attach()    // will do nothing and 'click' will not be bound
sqim.el.remove()
sqim.attach()    // now events are bound

from elstub

Sqimitive\Base doesn’t define any element-related functionality but only provides stubs for several common fields (el, render(), remove(), etc.) for standardized extension. See Sqimitive\jQuery.el for one such subclass.

Defined in: main.js, line 5021Show code

elEvents: {},
length

from readOnly

May only be read, not changed.

The number of _children nest()’ed into this object.

Just like $('p').length or bb:Collection-length.

See also slice().

Defined in: main.js, line 5030Show code

length: 0,

Methods

_ ( )

Part of: tag_Utilities

Returns wrapped _children array for chaining with your utility library.

Chaining is useful for transforming the value (array of children) several times. Call value() to obtain the final value. If you’re only doing a single transformation – just call it directly on this since most of them are exposed as methods (see util).

Note: Underscore (see un:chain()) and LoDash support chaining but NoDash doesn’t (no:COMPATIBILITY) and _() will error.

The reference to the utility library itself (the global _ object) is accessible via Sqimitive._.

Example
col._().filter(...).sort(...).value()    //=> array of children

// Same as:
var _ = Sqimitive._
_.chain(col.toArray()).filter(...).sort(...).value()

// Same as:
_.sort(col.filter(...), ...)

Defined in: main.js, lines 6613-6615 (3 lines) • Show code

_: function () {
  return _.chain(this.slice())
},
_defaultKey ( sqim )

Modifiers: protected

Part of: tag_Nesting

Return an automatic (implied) key for the given to-be child.

Result Types
Types Notes
string
number

_defaultKey() is called by nest() and assignChildren() to generate a key for the new child (sqim) that is about to be nested into this instance. Usage within base Sqimitive allows it to have side effects (such as to produce sequential keys).

Default Base’s implementation returns sqim’s _cid.

Make sure the returned value is constant for a given child so that if nesting the same child with no explicit key several times it isn’t constantly re-nested due to a different key (see nestEx()).

Warning: if you want to index children by some “ID” attribute (like Backbone’s bb:Collection does) note that _parentKey will not be automatically updated if that ID attribute changes. You should track it and update the collection. For example (see _childEvents):

var MyCollection = Sqimitive.Base.extend({
  _childEvents: ['.change_id'],

  events: {
    // '.change_id' will only occur for "models" which _parent is this.
    // They will be re-nested with the key expanding to
    // this._defaultKey(sqim), basically doing nest(newID, sqim) - and
    // since this is an _owning collection, nest() will cause sqim to
    // be unnest()'ed and then nested with the new key.
    '.change_id': function (sqim) { this.nest(sqim) },

    // Same but shorter using a masked function reference:
    '.change_id': 'nest.',

    '=_defaultKey': function (sup, sqim) {
      return sqim.get('id')
    },
  },
})

from unordered

Attention: JavaScript objects are unordered, as explained in the description of Ordered.

If you want to not just keep some attribute synced with _parentKey but also maintain a specific order based on it – use the Sqimitive\Ordered mixIn.

from inBB

In Backbone…

In Backbone, bb:Model-idAttribute names the property used for a similar purpose but in Sqimitive it’s a function allowing flexible automatic naming.

Defined in: main.js, lines 6177-6179 (3 lines) • Show code

_defaultKey: function (sqim) {
  return sqim._cid
},
assignChildren ( resp [, options] )

Part of: tag_Nesting

Unserializes children: updates own _children based on data of arbitrary format.

Merges external “response” resp into _children by updating existing nested sqimitives and adding new and/or removing unlisted ones. resp is a list of data objects, one object per every child:

resp = [ {caption: 'foo', body: 'bar'}, {caption: 'bazz'}, ... ]
         ^^^ the first child's data ^^  ^ the second child

options keys used by this method (context for all ...Func is this):

Arguments
Name Types Notes
newFuncfunction returning a constructed child; receives response data object (assignResp() is still called so it’s usually unneeded)
null/omitted create using _childClass of this
eqFuncnull/omitted if keepMissing then do nothing, else remove all children prior to assignment
string option name for get()
function (child, opt) returning true when the given child is the “same” as one of resp data objects and should be kept and have its _opt updated without creating a new child for that opt
keepMissingtrue to keep children “not present” in resp (as reported by eqFunc) unchanged
false to ensure all _children left after assignChildren() were present in resp and that others were unlist()’ed from self
keyFuncnull/omitted use _defaultKey() of this (returns _cid by default)
function returning key (string or number); receives a constructed Sqimitive (before it’s nest’ed) and resp data object
posFuncnull/omitted provide no explicit pos
function returning child’s pos’ition in Ordered.nestEx; same arguments as keyFunc
unFuncnull/omitted use unlist
function receiving a nested child plus some junk arguments; the child must become unnested when unFunc returns (no async operation)
Result Types
Types Notes
array [nested, unlisted] nested is an array of nestEx() return values, unlisted is an object of child sqimitives (by their keys in this) that were not found in resp.

Some members of nested may not belong to this. For example, if several resp objects were given the same key, only the last’s child will have _parent of this.

If options.eqFunc was unset then unlisted is either always {} (given a set options.keepMissing) or the copy of _children before assignChildren().

assignchreassignChildren() “unserializes” own _children objects while assignResp() “unserializes” own _opt’ions (“attributes”).

Example
var MyList = Sqimitive.Base.extend()

var MyItem = Sqimitive.Base.extend({
  _opt: {foo: ''},
})

var list = new MyList
var item1 = new MyItem({foo: 'existing'})

list.nest(item1)
var resp = [{foo: 'incoming#1'}, {foo: 'existing'}]
list.assignChildren(resp)
  //=> [ [item with 'foo' of 'incoming#1', item with 'foo' of 'existing'], {item1} ]
  // item1 was removed from list and the latter got two new children
  // with 'foo' of 'incoming#1' and 'existing' (the latter has
  // identical _opt with the removed item but a new object was
  // nevertheless created and nested)

// Let's restore the list.
list.invoke('remove')
list.nest(item1)

list.assignChildren(resp, {eqFunc: function (sqim, opt) {
  return sqim.get('foo') == opt.foo
}})
  //=> [ [item with 'foo' of 'incoming#1'], {} ]
  // Now item1 wasn't removed because the function we've passed
  // compared its foo option with resp's foo value and returned true
  // to indicate that existing child represents the same entity as the
  // incoming data object. In addition to item1, list got one new item
  // with foo of 'incoming#1'.

// We can use this short form of eqFunc if we're simply matching some
// option with a field by the same name in resp's objects:
list.assignChildren(resp, {eqFunc: 'foo'})
Example
list.assignChildren([], {keepMissing: true})
  //=> [ [], {} ]
  // nothing done as there were no items in resp

list.assignChildren([])
  //=> [ [], {old list._children} ]
  // the list was cleared as keepMissing defaults to false
Example
assignChildren([ {key: 'foo'}, {key: 'bar'} ], {eqFunc: 'key'})
  // adds/updates 2 children with _opt = {key: 'foo'} and {key: 'bar'};
  // "same" sqimitives are those having the same value for the 'key'
  // option and data object's 'key' field

assignChildren([ {key: 'foo'}, {key: 'bar'} ],
               {eqFunc: function (sqim, opt) { return sqim.get('key') == opt.key }})
  // identical to the above

assignChildren({ foosh: {key: 'foo'}, barrsh: {key: 'bar'} }, {eqFunc: 'key'})
  // identical to the above (resp is an object, keys ignored)

assignChildren({ foosh: {key: 'foo'}, barrsh: {key: 'bar'} },
               {eqFunc: 'key', keepMissing: true})
  // identical to the above but keeps old children

assignChildren([ {key: 'foo'}, {key: 'bar'} ])
  // similar but always removes all nested sqimitives and adds 2 new
  // ones

assignChildren(..., {onlyDefined: true, forceFire: true})
  // passes onlyDefined to assignResp() and forceFire to set()
ExampleNormally a child is constructed with no opt (in new/init) and gets them via a later assignResp(). Use newFunc to supply some opt’ions early but remember that for existing children (eqFunc) only assignResp() is called:
var data = [{foo: 1, bar: 2}, {foo: 3, bar: 4}]
assignChildren(data, {
  newFunc: resp => new ChildClass(_.pick(resp, 'foo', _.forceObject)),
  // Optionally, if we don't want foo to be considered by assignResp():
  schema: {foo: false},
  // Or, if a suitable schema is declared on ChildClass:
  schema: 'schemaProp',
})

The operation

First, assignChildren() determines which resp objects are new and which are “serialized” forms of one of the existing _children by calling options.eqFunc with combinations of (child, obj) for every child and data object. eqFunc should return true exactly for none or one such combination. If options.eqFunc is a string then it’s assumed that children are identified by an _opt’ion by this name (like “id”), mapping to data objects with the same value of this property:

var resp = [ {id: 1, caption: 'foo'}, {id: 2, caption: 'bar'} ]
var child = new Sqimitive.Base({id: 1, caption: 'quux'})
col.nest(child)
col.assignChildren(resp)
  // col now has 2 children: id=1 'foo', id=2 'bar'
  // but the first is a new object, not our child object even though
  // it's the same entity (given the same ID) - we should preserve it
  // since it may have event listeners set up by other objects

// But if we'd have done this:
col.assignChildren(resp, {eqFunc: 'id'})
  // col would have 2 children with the same _opt values
  // but the first would be the same child as nested in the beginning
  // so its event listeners would be preserved, just 'caption' updated

// String eqFunc is the same as:
col.assignChildren(resp, {eqFunc: function (child, obj) {
  return child.get('id') == obj.id
}})

To recap: children keys (_parentKey) are not used when linking _children and resp objects unless you code this in your options.eqFunc.

If options.eqFunc is unset then all existing children are unlist()’ed unless options.keepMissing is set – in this case they are preserved and for each item in resp a new child is nested. However, on duplicate keys (options.keyFunc) only the last child is kept and others are removed as per the usual behaviour of nest() (assignChildren() doesn’t handle this specially and such “removed-by-nesting” children are not reflected in the returned array).

Then, for data objects mapped to an existing child assignChildren() calls assignResp() on that child giving it the mapped data object to essentially update it. For unmapped, it creates a new Sqimitive by options.newFunc, calls assignResp() and nest-s it under the key of options.keyFunc.

Finally, if options.keepMissing is unset assignChildren() removes all children which were neither updated not created during this call. If it’s set then they are just left unchanged as _children of this.

Propagation of options

assignopropassignChildren() and assignResp() call/fire several methods/events (evt), all of which receive the same options object. This allows changing those methods’ options as well as propagating data back from them to the original caller (see optpropag).

In particular, assignChildren() calls assignResp(), set()/ifSet(), nestEx() firing normalize_OPT, change_OPT and change, as well as nestEx-produced events (however, for nestEx() a shallow-copy of options is given).

sqim.assignChildren({a: 1, b: 2}, {schema: api3, forceFire: true})
  // passes schema to assignResp() instead of _respToOpt and
  // passes forceFire to set() to always fire change events

Additionally, this method sets options.assignChildren to true so you can determine when an option is being set as a result of this method call:

Sqimitive.Base.extend({
  events: {
    change_caption: function (now, options) {
      // Don't refresh the view when unserializing.
      if (!options.assignChildren) { this.render() }
    },
  },
})

sqim.assignChildren([{caption: 'foo'}, ...])
sqim.render()    // call it once after finishing the update

Format of resp

resp is either an array of objects or an “object of objects” – then it’s converted using no:values() ignoring the keys. Use keyFunc if you want to have the object’s keys (or any other keys) assigned to children:

var resp = {"foo": {opt: 123}, "bar": {opt: 456}}
// Since assignChildren() discards resp's keys, copy them to each object:
_.each(resp, (opt, key) => opt._key = key)
col.assignChildren(resp, {keyFunc: (child, opt) => opt._key})

Sqimitive prior to v1.2 allowed resp to be an object with the data key to integrate with Python’s Flask framework but it’s no longer supported – see https://flask.palletsprojects.com/en/1.1.x/security/#json-security.

from unordered

Attention: JavaScript objects are unordered, as explained in the description of Ordered.

assignChildren() may process resp and nest/update children in any order. If you need specific order then pass resp as an array instead of object.

Other notes

  • There’s no assignChildren() version that takes ready-made objects (like Backbone’s bb:Collection-set()) because many questions arise: do you need to keep old duplicate children or replace them with supplied objects and what to do with event listeners on the old children if you do replace them, or with listeners on the new if you don’t; what to consider “duplicate” (some ID attribute? exact same object?), do you want to keep old options, etc. etc. Instead of making Sqimitive figure or enforce them on you it just lets you implement exactly what you need.
  • assignnameThis method is named “assign” to emphasize the fact that data may undergo transformations before being assumed by the sqimitive.

Defined in: main.js, lines 6929-6980 (52 lines) • Show code

assignChildren: function (resp, options) {
  options || (options = {})
  options.assignChildren = true
  var eqFunc = options.eqFunc
  var keyFunc = options.keyFunc || this._defaultKey
  var unFunc = options.unFunc || this.unlist

  if (eqFunc == null) {
    var retUnlisted = options.keepMissing ? {} : this.nested()
    options.keepMissing || this.forEach(unFunc, this)
  } else if (typeof eqFunc != 'function') {
    var field = eqFunc
    eqFunc = function (sqim, opt) { return opt && sqim.get(field) == opt[field] }
  }

  _.isArray(resp) || (resp = _.values(resp))
  var toRemove = eqFunc ? this.nested() : {}
  var nested = []

  for (var i = 0; i < resp.length; i++) {
    var found = false

    for (var key in toRemove) {
      if (eqFunc.call(this, toRemove[key], resp[i])) {
        toRemove[key].assignResp(resp[i], options)
        delete toRemove[key]
        found = true
        break
      }
    }

    if (!found) {
      // It's hard/impossible to tell apart a regular function and a
      // constructor so if you want to specify a class, create a function
      // that just returns that class. In ES6 that's simply () => MyClass.
      var child = options.newFunc
        ? options.newFunc.call(this, resp[i]) : new this._childClass
      child.assignResp(resp[i], options)
      var res = this.nestEx(_.assign({}, options, {
        key:    keyFunc.call(this, child, resp[i]),
        child:  child,
        pos:    options.posFunc
          ? options.posFunc.call(this, child, resp[i]) : null,
      }))
      // Can't use _parentKey if not _owning.
      nested.push(res)
    }
  }

  options.keepMissing || _.forEach(toRemove, unFunc, this)
  return [nested, retUnlisted /* when !eqFunc */ || toRemove]
},
assignResp ( resp [, options] )

Part of: tag_Options

Unserializes options: updates own _opt in batch based on a resp data object of arbitrary format.

assignResp() calls options.schema’s empty key and then set()-s own _opt based on transformation rules (options.schema) for the external input resp (e.g. an API response) – all of this in abatch(). resp is an object where one member usually represents one option (but this is not a requirement).

options keys used by this method:

Arguments
Name Types Notes
schemanull/omitted to use _respToOpt
object in _respToOpt format including empty {}
string own property name
onlyDefinednull/omitted to call set(key) for every key in resp not listed in schema
true to ignore such keys
Result Types
Types Notes
array of changed option names (as reported by ifSet() called by assignResp(), not by schema’s functions; doesn’t list .props)

from assignchre

assignChildren() “unserializes” own _children objects while assignResp() “unserializes” own _opt’ions (“attributes”).

ExampleIn the simplest case when _respToOpt is not overridden (i.e. is {}) and onlyDefined is unset assignResp() acts as a “multi-set()”:
sqim.assignResp({date: '2000-01-01', id_key: '123'})
  // without _respToOpt, schema and onlyDefined is equivalent to:
  // sqim.set('date', '2000-01-01').set('id_key', '123')

// Works the same regardless of sqim's _respToOpt - useful as mass-set().
sqim.assignResp({date: '2000-01-01', id_key: '123'}, {schema: {}})
ExampleassignrespvsUsing assignResp() in conjunction with _respToOpt to “blend” into this object a typical JSON response from a backend:
var MyModel = Sqimitive.Base.extend({
  _opt: {
    date: new Date(0),
    id_key: 0,
  },

  _respToOpt: {
    // Create a Date object from the incoming value:
    date: function (value, key) {
      return [key, new Date(value)]
    },

    // Pass the value through to _opt as is.
    id_key: true,
  },

  events: {
    change_id_key: 'render',

    '=normalize_id_key': function (sup, value) {
      value = parseInt(value)
      if (isNaN(value)) { throw new Error('What kind of ID is that?') }
      return value
    },
  },
})

var sqim = new MyModel

// Since it's using regular set() to assign new values all the usual
// normalize/change events are fired. In particular, MyModel's
// normalize_id_key and then change_id_key are called. As a result
// _opt setting happens exactly the same as if it was done with the
// usual set() from the outside.
sqim.assignResp({date: '2000-01-01', id_key: '123', bazzz: 'Ouch!'})

// Now sqim.get() is {date: new Date('2000-01-01'), id_key: 123, bazzz: 'Ouch!'}.
// date was turned into a Date object thanks to the transformation
// function in _respToOpt.
// id_key was turned into a number thanks to normalize_id_key.
// bazzz was assigned too because we didn't pass {onlyDefined: true}.
ExampleIf you have several API routes with different formats but logically the same data (so they unserialize to the same object), pass options.schema without changing _respToOpt on run-time:
sqim.assignResp(apiResp1, {schema: {id: 'id_key'}})
  // set('id_key', apiResp1.id) and set other keys as is

var schema = _.extend({extra: s => ['info', JSON.parse(s)]}, sqim._respToOpt)
sqim.assignResp(apiResp2, {schema})
  // set('extra', JSON.parse(apiResp2.info)) and
  // handle other keys according to sqim._respToOpt

If storing schema(s) within the object itself, set options.schema to the property name:

var MyModel = Sqimitive.Base.extend({
  apiSchema: {foo: 'bar'}
})
var obj = new MyModel
obj.assignResp(apiResp, {schema: 'apiSchema'})
// Equivalent to:
obj.assignResp(apiResp, {schema: obj.apiSchema})

schema is not meant for changing resp’s shape, e.g. from array [['opt', 'name'], ...] to object {opt: 'name', ...} – declare a specialized public method instead of making your consumers pass schema or know how to treat the data:

assignBillingResponse: function (resp) {
  this.assignResp(_.object(resp), {
    schema: this._billingRespToOpt,
    onlyDefined: true,
  })
}

Having options.onlyDefined unset is similar to having all keys in resp missing from _respToOpt listed there with the value of true:

Sqimitive.Base.extend({
  _respToOpt: {a: false, b: true},
})

sqim.assignResp({a: 1, b: 2, c: 3}, {onlyDefined: true})
  // _opt is {b: 2}

sqim.assignResp({a: 1, b: 2, c: 3})
  // _opt is {b: 2, c: 3}
  // as if _respToOpt had also {c: true}

ExampleSome Base methods internally call assignResp() with schema. If overriding this method, don’t change options for such calls. For example, to force skipping resp keys not defined in _respToOpt (handy if this method is used directly by API consumers, i.e. there’s no specialized “unserialize()”):
var MySqimitive = Sqimitive.Base.extend({
  events: {
    '=assignResp': function (sup, resp, options) {
      if (!options || !options.schema) {
        options = _.extend({}, options, {onlyDefined: true})
      }
      return sup(this, [resp, options])
    },
  },
})

Propagation of options

from assignoprop

assignChildren() and assignResp() call/fire several methods/events (evt), all of which receive the same options object. This allows changing those methods’ options as well as propagating data back from them to the original caller (see optpropag).

In particular, assignResp() calls set()/ifSet() firing normalize_OPT, change_OPT and change.

sqim.assignResp({a: 1, b: 2}, {forceFire: true})
  // passes forceFire to set() causing change events of a and/or b
  // to fire even if they had the same values before assignResp()

Additionally, this method sets options.assignResp to true so you can determine when an option is being set as a result of this method call (see assignoprop for an example). If called by assignChildren(), options has both assignChildren and assignResp set.

normalize/change vs _respToOpt

You may notice that =normalize_id_key and _respToOpt’s date in example assignrespvs fulfill a similar purpose. That example could have been written like this:

var MyModel = Sqimitive.Base.extend({
  _respToOpt: {
    date: function (value, key) {
      return [key, new Date(value)]
    },

    id_key: function (value, key) {
      value = parseInt(value)
      if (isNaN(value)) { throw new Error('What kind of ID is that?') }
      return [key, value]
    },
  },

  events: {
    change_id_key: 'render',
    // No '=normalize_id_key', moved to _respToOpt.
  },
})

Or like this:

var MyModel = Sqimitive.Base.extend({
  _respToOpt: {
    // No date, became normalize_date.
  },

  events: {
    change_id_key: 'render',

    '=normalize_date': function (sup, value) {
      return new Date(value)
    },

    '=normalize_id_key': function (sup, value) {
      value = parseInt(value)
      if (isNaN(value)) { throw new Error('What kind of ID is that?') }
      return value
    },
  },
})

Indeed, normalize_OPT and others are fired by ifSet() and they occur during assignResp() because the latter is calling set() internally. However, _respToOpt is only used by assignResp() so both of the above examples have issues:

  • If there is no =normalize_id_key then assignResp() works as expected while set('id_key', 'zabzab') doesn’t trigger an error.
  • If there are no _respToOpt rules then assignResp() again works as expected but set('date', new Date) results in _opt.date becoming new Date(new Date).

Additionally, assignResp() allows different “unserialization profiles” by passing rules in options.schema rather than defining them in _respToOpt.

from unordered

Attention: JavaScript objects are unordered, as explained in the description of Ordered.

assignResp() may process resp and call set() in any order.

Other notes

from assignname

  • This method is named “assign” to emphasize the fact that data may undergo transformations before being assumed by the sqimitive.

Defined in: main.js, lines 7220-7256 (37 lines) • Show code

assignResp: function (resp, options) {
  return this.batch(null, function assignResp_() {
    options || (options = {})
    options.assignResp = true

    var changed = []
    var schema = this[options.schema] || options.schema || this._respToOpt

    var set = schema[''] && schema[''].call(this, resp, options) || {}
    _.forEach(set, function (v, k) {
      this.ifSet(k, v, options) && changed.push(k)
    }, this)

    for (var key in resp) {
      var value = resp[key]
      var opt = schema[key]
      opt === undefined && (opt = !options.onlyDefined)
      opt === true && (opt = key)

      if (typeof opt == 'function') {
        opt = opt.call(this, value, key, resp, options) || [false]
        value = opt[1]
        opt = opt[0]
      }

      if (opt !== false) {
        if (opt[0] == '.' && key[0] != '.') {
          this[opt.substr(1) || key] = value
        } else if (this.ifSet(opt, value, options)) {
          changed.push(opt)
        }
      }
    }

    return changed
  })
},
attach ( parent )

Part of: tag_Lifecycle

Attach the object to its parent and bind “external” event handlers (elEvents).

from renderAttach

Result Types
Types Notes
baseAttachobject this

Here’s a typical Sqimitive object lifecycle:

  • construct with new: new Class({opt...})
  • attach() (to DOM, etc.), for members – when nested to a collection
  • render() when required for user to see it first time
  • render() again when something changes that affects the visual presentation (usually in response to a change of some _opt’ion)
  • finally, remove()

Complete re-rendering on change is simple and adequate for many simple sqimitives but if it’s heavy, it’s customary to perform a partial update using method(s) named update(). There are no such methods by default but see vw for an example.

Example
var Label = Sqimitive.jQuery.extend({
  _opt: {caption: 'untitled'},

  events: {
    render: function () {
      this.el.text(this.get('caption'))
    },

    change_caption: 'render',
  },
})
                                    // Lifecycle:
;(new Label)                        // 1) construct
  .attach('body')                   // 2) attach
  .render()                         // 3) render
  .set('caption', 'render again!')  // 4) update

from elstub

Sqimitive\Base doesn’t define any element-related functionality but only provides stubs for several common fields (el, render(), remove(), etc.) for standardized extension. See Sqimitive\jQuery.el for one such subclass.

Base’s attach() does nothing. You may be looking for Sqimitive\jQuery.attach().

Defined in: main.js, lines 5250-5252 (3 lines) • Show code

attach: function (parent) {
  return this
},
bubble ( func, args, onSelf )

Part of: tag_Events

Calls the method func with arguments args on every _parent (recursively, if any) and optionally onSelf.

Result Types
Types Notes
bubsinthis

If onSelf is true then first calls func on this, if defined.

bubble() proceeds upstream while sink() descends downstream. However, the first walks only _owning sqimitives (since it’s using _parent) while the second works for non-_owning too (since it’s using _children).

You can “recursively” fire events as well since events typically create methods (evt; see _wrapHandler()’s source code for details).

bubble() is very much like DOM’s event bubbling except that it happens on sqimitives, not on their el’s.

While it should not be abused because it makes the execution flow less obvious (much like goto or longjmp()), it’s indispensible for propagating generic signals like errors and logs to whoever is on top.

Example
// Causes self and all parent sqimitives to be rendered:
sqim.bubble('render', [], true)

// Recursively calls invoke('render') on all parents which results
// in them calling attach() on children (given the default behaviour
// of attach()):
sqim.bubble('invoke', ['attach'])

// We can use it to obtain data from "some" (unspecified) _owning
// object:
var out = {}
sqim.bubble('giveMeData', [out])
alert(out.data)
// The above will work if any parent has a handler like this:
parent.on('giveMeData', function (out) { out.data = 'here goes' })

Defined in: main.js, lines 7304-7308 (5 lines) • Show code

bubble: function (func, args, onSelf) {
  onSelf && this[func] && this[func].apply(this, args)
  this._parent && this._parent.bubble(func, args, true)
  return this
},
change ( opt, now, old, options )

Part of: tag_Options

Called to notify that the value of some _opt’ion (opt) has changed from old to now.

from changeAndOpt

ifSet normalizes (normalize_OPT) new option’s value (now) and, if it’s different from the current one (old) fires an event named “change_” + the option’s name (change_OPT), then fires change.

New value is already written to this._opt.OPT by the time change events occur.

from normopt

options is the object originally given to set() or ifSet().

With new being a reserved word, it’s customary to name the first argument “now” rather than “value” to clearly indicate which argument is the new value (“now” and “old”).

Example
var MyClass = Sqimitive.Base.extend({
  _opt: {
    caption: '',
  },

  events: {
    // When _opt.caption is changed - call render() to update the looks.
    change_caption: 'render', //¹
  },
})

¹ Be aware of argDanger as your handler might care for extra arguments. If it does then use a masked reference (see masker()):

events: {
  // Pass no arguments thanks to '-':
  change_caption: 'updateCaption-',
},

Example
var MyClass = Sqimitive.jQuery.extend({
  el: {tag: 'form'},

  _opt: {
    caption: '',
    body: '',
  },

  events: {
    // When any option is changed - update the corresponding <input>.
    change: function (name, now) {
      // change_OPT doesn't receive option's name as the first argument.
      //
      // now is used as is - if clean-up is required then normalize_OPT
      // events must be handled.
      this.$('[name="' + name + '"]').val(now)
    },
  },
})
ExamplesetoptpropagPropagation of options (optpropag):
sqim.on('change_foo', function (now, old, options) {
  if (!options.noSync) {
    $.ajax({
      url: 'update',
      type: 'POST',
      data: this.get(),
    })
  }
})

// The handler above performs an AJAX request:
sqim.ifSet('foo', 123)

// But not now (set() passes options through to ifSet()):
sqim.set('foo', 123, {noSync: true})

// assignResp() passes options to set() so no request is performed too:
sqim.assignResp({foo: 123}, {noSync: true})

Defined in: main.js, line 5704

change_OPT ( now, old, options )

Part of: tag_Options

Called to notify that the value of _opt’ion named OPT has changed from old to now.

changeAndOptifSet normalizes (normalize_OPT) new option’s value (now) and, if it’s different from the current one (old) fires an event named “change_” + the option’s name (change_OPT), then fires change.

New value is already written to this._opt.OPT by the time change events occur.

from normopt

options is the object originally given to set() or ifSet().

With new being a reserved word, it’s customary to name the first argument “now” rather than “value” to clearly indicate which argument is the new value (“now” and “old”).

Example
var MyClass = Sqimitive.Base.extend({
  _opt: {
    caption: '',
  },

  events: {
    // When _opt.caption is changed - call render() to update the looks.
    change_caption: 'render', //¹
  },
})

¹ Be aware of argDanger as your handler might care for extra arguments. If it does then use a masked reference (see masker()):

events: {
  // Pass no arguments thanks to '-':
  change_caption: 'updateCaption-',
},

Example
var MyClass = Sqimitive.jQuery.extend({
  el: {tag: 'form'},

  _opt: {
    caption: '',
    body: '',
  },

  events: {
    // When any option is changed - update the corresponding <input>.
    change: function (name, now) {
      // change_OPT doesn't receive option's name as the first argument.
      //
      // now is used as is - if clean-up is required then normalize_OPT
      // events must be handled.
      this.$('[name="' + name + '"]').val(now)
    },
  },
})
ExamplesetoptpropagPropagation of options (optpropag):
sqim.on('change_foo', function (now, old, options) {
  if (!options.noSync) {
    $.ajax({
      url: 'update',
      type: 'POST',
      data: this.get(),
    })
  }
})

// The handler above performs an AJAX request:
sqim.ifSet('foo', 123)

// But not now (set() passes options through to ifSet()):
sqim.set('foo', 123, {noSync: true})

// assignResp() passes options to set() so no request is performed too:
sqim.assignResp({foo: 123}, {noSync: true})

Defined in: main.js, line 5613

constructor ( [opt] )

Modifiers: constructor

Calls Core.constructor() and fire()-s init and postInit, passing opt to all.

opt is the first argument optionally given to new: new Sqimitive.Base({opt...}).

This method ensures opt is always an object before passing it on so there is no need for checks like (opt && opt.foo).

Exampleinit/postInit handlers may propagate changes in user-given opt to other handlers or even to the caller of new (optpropag).
var MyClass = Sqimitive.Base.extend({
  events: {
    init: function (opt) { opt.changed = 123 },
  },
})

var opt = {}
;(new MyClass(opt))
  // opt.changed == 123

Constructors are reminiscents of the traditional JavaScript OOP (if it can be called so). They are hard to work with in Sqimitive (you can’t override them using events) so you want to leave them alone, instead working with init() and postInit() which are “regular” Sqimitive methods.

Defined in: main.js, lines 5062-5073 (12 lines) • Show code

constructor: function Sqimitive_Base(opt) {
  // ^^ Giving this function a name so that it's visible in the debugger.

  // Ensuring the argument is always an object.
  // Mere arguments[0] = {} won't work because if arguments.length == 0,
  // this won't update length and so apply() will assume arguments is still
  // empty (0) even though index 0 has been set.
  opt || ap.unshift.call(arguments, {})
  Sqimitive.Base.__super__.constructor.apply(this, arguments)
  this.init.apply(this, arguments)
  this.postInit.apply(this, arguments)
},
findKey ( sqim | func [, cx] )

Part of: tag_Nesting, tag_Utilities

Returns the string key of the given child (in _children of self) or of the first child matched by the callback, or undefined.

findKey() has two call forms:

  • function (sqim) – If sqim is part of _children, returns its key.
  • function (func [, cx]) – Call func in the cx context (this if null or omitted) giving it the usual iterator’s set of arguments: childObject, childKey, childrenObject (as in Underscore, etc.). Returns childKey as soon as func returns a truthy value.

    Warning: do not modify childrenObject as it’s the _children itself.

In any case, findKey() returns undefined if no match was found.

Example
col.findKey(col.first())   // get key of the first child
col.findKey(ch => ch.get('enabled'))   // get key of the first "enabled" child

Other notes:

from parentkeyowning

Note: key only matches the child’s _parentKey if this is _owning, else it may differ.

from unordered

Attention: JavaScript objects are unordered, as explained in the description of Ordered.

Either func() should match exactly 0 or 1 children or the caller should not care which of the matched ones findKey() returns (since it may return a different matching child every time).

Defined in: main.js, lines 6541-6553 (13 lines) • Show code

findKey: function (func, cx) {
  var eq = func instanceof Object
  if (eq && this._owning) {
    return func._parent === this ? func._parentKey : undefined
  }
  for (var key in this._children) {
    if (eq
          ? this._children[key] === func
          : func.call(cx || this, this._children[key], key, this._children)) {
      return key
    }
  }
},
get ( [opt] )

Part of: tag_Options

Returns the value of one _opt or values of all of them (if no argument).

Example
sqim.get('opt1')   //=> 'value'
sqim.get()         //=> {opt1: 'value', opt2: 123, ...}

All options’ values are returned in an object shallow-copied from _opt meaning it’s safe to change the object itself (add/remove properties) but changing non-scalar values will indirectly change options inside the object:

var MyClass = Sqimitive.Base.extend({
  _opt: {array: [1, 2]},
})

var obj = new MyClass
var opts = obj.get()   //=> {array: [1, 2]}
opts.new = 3
obj.get()              //=> {array: [1, 2]} - same
opts.array.push(4)
obj.get()              //=> {array: [1, 2, 4]} - changed

Override this method to read non-existing options or transform them like this:

var MySetter = Sqimitive.Base.extend({
  _opt: {
    foo: 'foo!',
  },

  events: {
    // Now any option can be read as option:up to turn its value into
    // upper case.
    '+get': function (res, name) {
      if (name = name.match(/^([^:]+):up$/)) {
        return this.get(name[1]).toUpperCase()
      }
    },
  },
})

alert((new MySetter).get('foo'))       //=> foo!
alert((new MySetter).get('foo:up'))    //=> FOO!

Other notes:

  • Cloning a sqimitive is often as easy as doing new MySqim(orig.get()) sans _initToOpt considerations. Don’t use clone orig (new object will have wrong state, e.g. references to the original _parent).

from getnset

  • get() and set() (ifSet()) may be seen as public or private API at user’s discretion. A complex class may opt for wrappers around these, discouraging its users from direct access to options:
    title: function () { return this.get('title') }
  • See also Options overview (opt).

from getmul

  • Use getSet(['foo', 'bar']) to retrieve multiple properties as an array. Use _.pick(this.get(), 'foo', 'bar', _.forceObject) to retrieve them as an object.

from unordered

Attention: JavaScript objects are unordered, as explained in the description of Ordered.

There are no guarantees in which order options would be iterated over.

Defined in: main.js, lines 5459-5461 (3 lines) • Show code

get: function (opt) {
  return arguments.length ? this._opt[opt] : _.assign({}, this._opt)
},
getSet ( toGet [, toSet [, toReturn]] [, func[, cx]] )

Part of: tag_Options

Performs batch’ed get() and/or set() on multiple options.

Arguments
Name Types Notes
toGetstring _opt name
array of names
toSetstring _opt name
array of names
null/omitted assume toGet
toReturnstring _opt name Values for names prefixed with - are obtained before setting toSet (- is ignored by toGet/toSet).
array of names
null/omitted assume toSet
func Given G arguments (G = length of toGet), returns an array or a single value (if toSet is a string) – option value(s) to set. Returned array’s length must be ≤ length of toSet. Missing members of toSet are not set.

If func is missing sets every toSet[N] to toGet[N]. The length of toSet must be ≤ length of toGet.

cxobject The context for func
null/omitted use this
Result Types
Types Notes
array of values if toReturn is an array
mixed single value if not

Other notes:

  • Errors if called without arguments (without toGet).
  • getmulUse getSet(['foo', 'bar']) to retrieve multiple properties as an array. Use _.pick(this.get(), 'foo', 'bar', _.forceObject) to retrieve them as an object.
Example
// Multi-get() - but ensure given arrays have no properties beginning with '-':
sqim.getSet(['opt1', 'opt2', 'opt3'])
  //=> [value of opt1, value of opt2, value of opt3]
  // Even though the above not only get()-s but also set()-s them,
  // unless a normalize_OPT returns a different value then no change
  // events are fired since each option is set to its current value.

// Returns new value of opt2 (same as value of opt1):
sqim.getSet('opt1', 'opt2')
// Similar but returns this:
sqim.set('opt2', sqim.get('opt1'))
// Similar but returns value of opt3, not opt2 (opt2 == opt1):
sqim.getSet('opt1', 'opt2', 'opt3')

// Value exchange:
sqim.getSet(['left', 'right'], ['right', 'left'])
// Equivalent to:
var temp = sqim.get('left')
sqim.set('left', sqim.get('right'))
sqim.set('right', temp)

// Temporary change:
var old = sqim.getSet(['foo', 'bar'])
try {
  // ...
} finally {
  sqim.getSet(['foo', 'bar'], () => old)
}
ExampleUsage of -:
// toReturn defaults to toSet which defaults to toGet. Returns old + 1:
var newRound = sqim.getSet('round',  old => old + 1)

// Dash prefix in toReturn causes pre-toSet values to be returned (i.e. old):
var oldRound = sqim.getSet('round', 'round', '-round', old => old + 1)

// Given dashes in toGet/toSet are ignored, this is an equivalent:
var oldRound = sqim.getSet('-round', old => old + 1)
ExampleUpdating an option based on another option:
Sqimitive.Base.extend({
  _opt: {money: 0, income: 10},
})

var newMoney = sqim.getSet(['money', 'income'], 'money',
                           (money, income) => money + income)
alert(newMoney)    //=> 10

// Equivalent to:
var newMoney = sqim.get('money') + sqim.get('income')
sqim.set('money', newMoney)
alert(newMoney)
ExampleCommon idiom of re-applying normalize_OPT():
Sqimitive.Base.extend({
  _opt: {position: 0, max: 0},

  events: {
    change_max: function (now) {
      this.getSet('position')
    },

    '+normalize_position': function (res, now) {
      return Math.min(now, this.get('max'))
    },
  },
})

sqim.set('position', 10)
sqim.set('max', 5)
sqim.get('position')    //=> 5

Defined in: main.js, lines 5368-5393 (26 lines) • Show code

getSet: function (toGet, toSet, toReturn, func, cx) {
  var args = Core.toArray(arguments)
  for (var i = 0; i <= 2; i++) {
    if (typeof args[i] == 'function') {
      args.splice(i, 0, args[i - 1])
    } else if (args[i] == null) {
      args[i] = args[i - 1]
    }
    // Array version of 1st/2nd/3rd argument, 0/1 with removed '-' prefix.
    var a = args['a' + i] = _.isArray(args[i]) ? args[i] : [args[i]]
    i < 2 && a.forEach(function (s, i) { a[i] = s.substr(s[0] == '-') })
  }
  var got = _.map(args.a0, this.get, this)
  var preGot = {}
  args.a2.forEach(function (opt, i) {
    opt[0] == '-' && (preGot[i] = this.get(opt.substr(1)))
  }, this)
  if (args[3]) {
    got = args[3].apply(args[4] || this, got)
    if (!_.isArray(args[1])) { got = [got] }
  }
  this.assignResp(_.object(args.a1, got), {schema: {}})
  return _.isArray(args[2])
    ? _.assign(_.map(args[2], this.get, this), preGot)
    : 0 in preGot ? preGot[0] : this.get(args[2])
},
ifSet ( opt, value[, options] )

Part of: tag_Options

Changes the value of one _opt’ion and returns true if it was different from current.

Result Types
Types Notes
true if events were fired (value different or options.forceFire set)
false otherwise
Example
sqim.ifSet('key', 'foo')   //=> true (changed)
sqim.ifSet('key', 'foo')   //=> false (unchanged)
sqim.ifSet('key', 'foo', {forceFire: true})    //=> true (forced change)

ifSetSetFires normalize_OPT giving it value; if the result is not isEqual to this._opt[opt] or if options.forceFire is set – changes _opt and fires batch’ed change_OPT event, then change.

ExampleYou can take advantage of ifSet()’s return value to perform interlocking operations saving a call to get():
if (sqim.ifSet('eventsBound', true)) {
  // eventsBound was previously not isEqual() and it was now changed to
  // true so we can do what we need, once until it changes to non-true
  // again.
}
…As a short form of writing:
if (!sqim.get('eventsBound')) {
  sqim.set('eventsBound', true)
  // ...
}

Other notes:

  • Avoid forceFire because it calls not only your object’s handlers (which you expect) but also other instances’ (which you usually don’t).
  • Use set() which returns this if you don’t care for the return value but want method chaining.
  • Use getSet() if you want to update options based on other options (e.g. increment a value).
  • Use batch() to assign related options and defer on-change behaviour.
  • There’s no set() version that writes multiple options at once. You might be looking for assignResp(), possibly with schema (useful when assigning an API response): assignResp(opts, {schema: {}}) - equivalent to batch’ed bunch of set()-s. Generally, consider assignResp() if you find yourself writing a series of set().
  • It is safe to change _opt from within normalize_OPT or change handlers – they are written to this._opt immediately but subsequent change_OPT and change events are batch’ed in FIFO fashion (first set – first fired). This preserves “incremental” update order but also means that set() is not always immediately followed by a call to these handlers.
  • Because ifSet() creates an implicit batch, change_OPT and change fired for OPT receive the same options.batchID even if you don’t wrap the ifSet() call into a batch().
  • getnsetget() and set() (ifSet()) may be seen as public or private API at user’s discretion. A complex class may opt for wrappers around these, discouraging its users from direct access to options:
    title: function () { return this.get('title') }
  • See also Options overview (opt).
ExampleOverriding ifSet() is possible but most of the time normalize_OPT is what you need:
var MySetter = Sqimitive.Base.extend({
  _opt: {
    readOnly: 'foo',
  },

  events: {
    // Our handler will be called before the inherited ifSet() and
    // will prevent modification of this._opt.readOnly when they are
    // done via set/ifSet (i.e. the recommended way).
    //
    // Make sure not to hook '-set' because set() calls ifSet() and it
    // would be still possible to change 'readOnly' with
    // ifSet('readOnly', 'bar').
    '-ifSet': function (opt) {
      if (opt == 'readOnly') {
        throw new Error('You shouldn\'t really change what is called readOnly, you know?')
      }
    },

    // normalize_OPT would be better though:
    normalize_readOnly: function () {
      throw new Error('You shouldn\'t change what is called readOnly, you know!')
    },
  },
})
Hooking ifSet() specifically allows immediate, not batch’ed reaction (but consider using priority^ – see evtref). options will lack some fields at this point though (_batchOptions()).
'-ifSet': function (opt, value, options) {
  console.log('Option ', opt, ' is about to change to ', value)
},

optpropagoptions can be used to propagate custom data to the handlers of normalize_OPT(), change_OPT() and change(), and even back from them (options is always an object, possibly empty):

Sqimitive.Base.extend({
  change_foo: function (now, options) {
    options.foo = 123
  },
})

var options = {}
sqim.set('foo', options)
alert(options.foo)       //=> 123

Defined in: main.js, lines 5841-5876 (36 lines) • Show code

ifSet: function (opt, value, options) {
  options || (options = {})
  var old = this._opt[opt]
  // This should be an event but for sloppy code check and call directly if
  // it's a method.
  var func = 'normalize_' + opt
  if (func in this) {
    this[func] && (norm = this[func](value, options))
  } else {
    // Hooks may exist with no func if _wrapUndeclared is false.
    var norm = this.fire(func, [value, options])
  }
  // undefined result can be made special since normalize_OPT is usually a
  // firer() and there's (almost) no way to return undefined from a hook
  // (see Core.fire()). If we did get undefined it means no hooks returned
  // any value, i.e. the original value is good.
  //
  // Consider this example where the only normalize_foo hook is used to
  // validate the value, not normalize it:
  //
  //   events: {
  //     normalize_foo: function (now) {
  //       if (!now) { throw new Error(...) }
  //     },
  norm === undefined || (value = norm)

  if (options.forceFire || !this.isEqual(value, old)) {
    this._opt[opt] = value
    this.batch(null, function (id) {
      options = this._batchOptions(id, options)
      this._batch.push(['change_' + opt, value, old, options])
      this._batch.push(['change',   opt, value, old, options])
    })
    return true
  }
},
init ( [opt] )

Part of: tag_Lifecycle

Resolves _childClass and sets _opt’ions from opt.

Arguments of init() and postInit() that follows it match those given to the constructor, which in turn gets them from new. Usually only the first one (opt) is used but you can use others:

var MyClass = Sqimitive.Base.extend({
  events: {
    init: function (opt, extra, fooArray) { ... },
  }
})

new MyClass({opt...}, 'extra', ['foo'])

Options are set in batch by giving assignResp() opt (or {}) and schema of _initToOpt. By default, _opt keys missing from opt remain with the declaration-time values while opt.el is ignored even if present.

Other notes:

  • init() stores resolved relative _childClass in the prototype of this so subsequent constructors of this’ class no longer need to do it.
  • initonceBoth init() and postInit() are only called once in a given object’s lifetime.

from plainstub

  • This method returns nothing.
  • It should not be called directly.
Example
var MyClass = Sqimitive.Base.extend({
  _opt: {a: 1, b: 2},
})

new MyClass({b: 3, c: 4})
  // _opt is {a: 1, b: 3, c: 4}

Defined in: main.js, lines 5121-5138 (18 lines) • Show code

init: function (opt) {
  var childClass = this._childClass

  // Can't be string - converted to array by Core::extend().
  if (_.isArray(childClass)) {
    var path = childClass[1].split('.')
    childClass = childClass[0]
    while (childClass && path[0]) {
      childClass = childClass[path.shift()]
    }
    if (!childClass) {
      throw new ReferenceError('init: _childClass by path not found')
    }
    this.constructor.prototype._childClass = childClass
  }

  this.assignResp(opt, {schema: '_initToOpt'})
},
isEqual ( a, b )

Part of: tag_Utilities

Returns true if both arguments are “same enough” and ifSet() should not change _opt and fire change_OPT.

Default implementation compares arguments using strict equality operator (===).

Remember that in JavaScript, NaN !== NaN so sqim.set('num', NaN) will fire change_OPT even if num is already NaN.

ExampleYou can override isEqual() to use == if you’re ready for flaky behaviour (see no:isEqual()). un:isEqual() is another option.
var MyBase = Sqimitive.Base.extend({
  events: {
    '=isEqual': function (sup, a, b) {
      return _.isEqual(a, b)
    },
  },
})

from normiseq

ExampleifSet() fires normalize_OPT, then isEqual(). The first operates in context of specific _opt’ion and has its result interpreted by isEqual(), which in turn is option-agnostic.

For example, if foo is an unordered array of numbers then normalize_OPT can prevent change_OPT if new value has the same numbers by returning current value which by default isEqual (===) to itself:

'+normalize_foo': function (res, value) {
  return (value = _.unique(value)) + '' == this.get('foo') ? this.get('foo') : value
},
On the other hand, isEqual() may prevent NaN from being inequal to itself, for all _opt’ions:
'+isEqual': function (res, a, b) {
  return res || (isNaN(a) && isNaN(b))
},

Defined in: main.js, lines 5904-5906 (3 lines) • Show code

isEqual: function (a, b) {
  return a === b
},
nest ( [key, ] sqim|opt [, options] )

Part of: tag_Nesting

Adds a new child using a shorter syntax than nestEx().

Result Types
Types Notes
sqim The added child.
Arguments
Name Types Notes
keystring If omitted, _defaultKey() is used which by default returns sqim’s _cid (unique instance identifier).
number
omitted
sqimobject New child to nest.
optobject In place of sqim, only when _childClass is not Object – a plain {} object of opt’ions for _childClass’ constructor.
optionsobject Keys key and child are set by nest()
null/omitted
Examplesqim vs opt:
var Parent = Sqimitive.Base.extend({
  // Inherited _childClass is Sqimitive.Base.
})

var parent = new Parent
parent.nest(new Sqimitive.Base({foo: 123}))
// Equivalent to:
parent.nest(new parent._childClass({foo: 123}))
// Equivalent to:
parent.nest({foo: 123})

// Constructing with default (no) opt:
parent.nest(new Sqimitive.Base())
// Equivalent to:
parent.nest({})

If you are overriding the “nesting” behaviour you should override nestEx instead of nest which calls the former.

from nestExDesc

Example
sqim.nest(new Sqimitive.Base)          // _parentKey = 'p123' (the _cid)
sqim.nest('key', new Sqimitive.Base)   // _parentKey = 'key'
// Same:
sqim.nestEx({key: 'key', child: new Sqimitive.Base})

sqim.unlist('key')
  // if sqim._owning is false - removes the child under 'key' if any,
  // else calls sqim.remove(); returns the found child or undefined
ExampleWhen hooking nestEx() to listen for changes (newly added sqimitives), check options.changed to avoid triggering your update logic if child was already nested:
Sqimitive.Base.extend({
  events: {
    // WRONG: will re-render even if the child was already there:
    nestEx: 'render',

    // CORRECT:
    '+nestEx': function (options) {
      if (options.changed) { this.render() }
    },

    // CORRECT since nestEx() returns the same options object as
    // given as its first argument:
    nestEx: function (options) {
      if (options.changed) { this.render() }
    },
  },
})

var child = new Sqimitive.Base
col.nest(child)   // calls render()
col.nest(child)   // doesn't call
  // child is already under the same key (_defaultKey() returns
  // _cid which is constant for the lifetime of an object)

Other notes:

  • There’s no nest() version that adds multiple children at once as you would do in some kind of “sync” operation. You might be looking for assignChildren() (useful when assigning an API response).
  • As with other options-accepting methods (e.g. ifSet()), the options object may be used to propagate custom data to event listeners (optpropag); options are also passed through by assignChildren().

Internal operation

nestEx() checks and errors if options.key is an object of any type (including null or undefined), or if options.child is of a wrong class (not _childClass). Then it converts and sets options.key to string, sets options.previous to the child currently occupying key (possibly undefined) and sets options.changed to false if previous is exactly child.

If options.changed is false then exits, otherwise:

Finally, if the child was nested (options.changed set), nestEx() _forward-s its _childEvents and, if _owning, calls new child’s owned() to notify it of the parent change.

Observe that nestEx():

  • Does nothing if child is already contained in this instance under the same key, i.e. when options.previous == child (changed is false).
  • unnest-s and nests child again if key differs.

Defined in: main.js, lines 5950-5965 (16 lines) • Show code

nest: function (key, sqim, options) {
  var sqimIndex = +!(key instanceof Object)
  if (arguments[sqimIndex].constructor === Object &&
      this._childClass !== Object) {
    // function (..., opt, ...)
    arguments[sqimIndex] = new this._childClass(arguments[sqimIndex])
  }
  if (!sqimIndex) {   // function ( sqim|opt [, options] )
    ap.unshift.call(arguments, this._defaultKey(arguments[0]))
  }
  options = _.assign({}, arguments[2] || {}, {
    key:    arguments[0],
    child:  arguments[1],
  })
  return this.nestEx(options).child
},
nestEx ( options )

Part of: tag_Nesting

Adds a new child to self (_children), unnest’ing the old one at the same key (if any).

Result Types
Types Notes
object options with added details about the operation.

The caller must set these keys in options:

Arguments
Name Types Notes
childobject A sqimitive to nest.
keystring New options.child’s key in _children
number

nestEx() mutates and returns options with updated keys:

Arguments
Name Types Notes
keystring always
previousobject
undefined
changedbool Whether any changes were done to the collection.

Subclasses and mixIn-s can use other options keys – for example, Sqimitive\Ordered receives insertion order in options.pos and sets options.index on return.

After the call, old length of self could be determined as follows:

this.length - (options.previous && options.changed)

In most cases nest() is more convenient to use as it allows omitting options.key and avoiding object notation for options.

ExamplenestExDesc
sqim.nest(new Sqimitive.Base)          // _parentKey = 'p123' (the _cid)
sqim.nest('key', new Sqimitive.Base)   // _parentKey = 'key'
// Same:
sqim.nestEx({key: 'key', child: new Sqimitive.Base})

sqim.unlist('key')
  // if sqim._owning is false - removes the child under 'key' if any,
  // else calls sqim.remove(); returns the found child or undefined
ExampleWhen hooking nestEx() to listen for changes (newly added sqimitives), check options.changed to avoid triggering your update logic if child was already nested:
Sqimitive.Base.extend({
  events: {
    // WRONG: will re-render even if the child was already there:
    nestEx: 'render',

    // CORRECT:
    '+nestEx': function (options) {
      if (options.changed) { this.render() }
    },

    // CORRECT since nestEx() returns the same options object as
    // given as its first argument:
    nestEx: function (options) {
      if (options.changed) { this.render() }
    },
  },
})

var child = new Sqimitive.Base
col.nest(child)   // calls render()
col.nest(child)   // doesn't call
  // child is already under the same key (_defaultKey() returns
  // _cid which is constant for the lifetime of an object)

Other notes:

  • There’s no nest() version that adds multiple children at once as you would do in some kind of “sync” operation. You might be looking for assignChildren() (useful when assigning an API response).
  • As with other options-accepting methods (e.g. ifSet()), the options object may be used to propagate custom data to event listeners (optpropag); options are also passed through by assignChildren().

Internal operation

nestEx() checks and errors if options.key is an object of any type (including null or undefined), or if options.child is of a wrong class (not _childClass). Then it converts and sets options.key to string, sets options.previous to the child currently occupying key (possibly undefined) and sets options.changed to false if previous is exactly child.

If options.changed is false then exits, otherwise:

Finally, if the child was nested (options.changed set), nestEx() _forward-s its _childEvents and, if _owning, calls new child’s owned() to notify it of the parent change.

Observe that nestEx():

  • Does nothing if child is already contained in this instance under the same key, i.e. when options.previous == child (changed is false).
  • unnest-s and nests child again if key differs.
  • nestdupAlways adds child if this is non-_owning so it may duplicate in _children. To avoid this, hook =nestEx and call sup (evtpf) only if !this.includes(options.child) (util).

Defined in: main.js, lines 6077-6111 (35 lines) • Show code

nestEx: function (options) {
  var sqim = options.child

  if (!(sqim instanceof this._childClass)) {
    throw new TypeError('nestEx: Nesting Sqimitive of wrong class')
  } else if (typeof options.key == 'object' ||    // object or null
             options.key === undefined) {
    throw new TypeError('nestEx: Bad key given')
  }

  // Object keys are always strings; _parentKey mismatching actual key will
  // break indexOf() if it's used on an array like _.keys(this._children).
  var key = options.key += ''
  var prev = options.previous = this._children[key]

  if (options.changed = prev !== sqim) {
    if (this._owning) {
      prev && prev.unnest()   // --this.length
      sqim.unnest()
      this._children[key] = sqim
      sqim._parent = this
      sqim._parentKey = key
      this.length++
    } else {
      this._children[key] = sqim
      prev ? this.unnested(prev, key) : this.length++
    }

    this._forward('.', this._childEvents, sqim)
    this._owning && sqim.owned()
  }

  this._nested(options)
  return options
},
nested ( [key] )

Part of: tag_Nesting

Returns a single child by key or instance or all _children (if no argument).

Result Types
Types Notes
object all children if key not given
object the found child
undefined if given key/child isn’t nested in this
Arguments
Name Types Notes
keyomitted return a shallow copy of _children
string or number return the object at that key (case-sensitive) or null
object return the argument itself if this object is nested as per findKey()
Example
sqim.nested()   //=> {childKey1: Sqimitive, ...}
sqim.nested('childKey1')    //=> Sqimitive by its key
sqim.nested('foobarbaz!')   //=> undefined - key not found in sqim._children

var child = sqim.nested('childKey1')
sqim.nested(child)          //=> child by its object
sqim.nested(new Sqimitive)  //=> undefined - object not listed in sqim._children
ExampleThe object key form allows using nested() as no:includes() (util) to check if a child is part of the collection:
if (col.nested(sqim)) {
  // ...
}

parentkeyowningNote: key only matches the child’s _parentKey if this is _owning, else it may differ.

from unordered

Attention: JavaScript objects are unordered, as explained in the description of Ordered.

Order of keys in the returned _children’s copy may be unpredictable.

nestedslicenested() without arguments returns an object – children with keys. slice() returns an array – children only, dropping their keys.

Defined in: main.js, lines 6424-6434 (11 lines) • Show code

nested: function (key) {
  if (!arguments.length) {
    return _.assign({}, this._children)
  } else if (key == null) {
    // Return undefined - neither keys nor children can be null.
  } else if (!(key instanceof Object)) {
    return this._children[key + '']
  } else if (this.findKey(key) != null) {
    return key
  }
},
normalize_OPT ( value, options )

Part of: tag_Options

Called to normalize and/or validate new _opt’ion value before it’s set. Returning undefined keeps it unchanged.

ifSet() fires an event named “normalize_” + the option’s name before the value is actually set to _opt. Here you should clean it up (e.g. trim whitespace) or throw an error if it has a wrong format (e.g. not YYYY-MM-DD for dates).

normoptoptions is the object originally given to set() or ifSet().

ExampleRemoving spaces and converting to lower case:
var MyNorm = Sqimitive.Base.extend({
  _opt: {
    stringie: '',
  },

  // Now 'stringie' is guaranteed to have no surrounding whitespace
  // and be lower case - as long as it's not written directly as
  // this._opt.stringie, which is a bad idea in general.
  normalize_stringie: function (value) {
    return _.trim(value).toLowerCase()
  },
})

var str = (new MyNorm)
  .set('stringie', '  Foo\n')
  .get('stringie')
    //=> 'Foo'
ExamplenormiseqifSet() fires normalize_OPT, then isEqual(). The first operates in context of specific _opt’ion and has its result interpreted by isEqual(), which in turn is option-agnostic.

For example, if foo is an unordered array of numbers then normalize_OPT can prevent change_OPT if new value has the same numbers by returning current value which by default isEqual (===) to itself:

'+normalize_foo': function (res, value) {
  return (value = _.unique(value)) + '' == this.get('foo') ? this.get('foo') : value
},
On the other hand, isEqual() may prevent NaN from being inequal to itself, for all _opt’ions:
'+isEqual': function (res, a, b) {
  return res || (isNaN(a) && isNaN(b))
},

ExampleValidating new value (see the sample To-Do application for a complete primer):
var MyValid = Sqimitive.Base.extend({
  _opt: {
    date: null,    //= Date or 'YYYY-MM-DD'¹
  },

  normalize_date: function (value) {
    if (!(value instanceof Date) &&
        (value = value.trim().match(/^(\d{4})-(\d\d)-(\d\d)$/))) {
      value = new Date(value[1], value[2] - 1, value[3])
    }
    if (!(value instanceof Date)) {
      throw new TypeError('Bad date format')
    }
    return value
  },
})

;(new MyValid).set('date', new Date)       // works
;(new MyValid).set('date', '2020-02-20')   // works
;(new MyValid).set('date', 'whatchadoin')  // TypeError

¹ It’s customary in Sqimitive to leave a comment explaining non-trivial options’ types or formats.

Unlike with change/change_OPT, there is no global normalization function (since every option usually needs a unique approach) but you can override ifSet() if you need this.

Remember: when defined in events, function’s return value is ignored unless = or + prefixes are used (evtpf). Also, see evtconc and es6this.

Sqimitive.Base.extend({
  events: {
    // WRONG: return value is ignored:
    normalize_stringie:    function (value) { return _.trim(value) },
    // CORRECT: adding handler after others:
    '+normalize_stringie': function (res, value) { return _.trim(value) },
    // CORRECT: adding handler instead of others:
    '=normalize_stringie': function (sup, value) { return _.trim(value) },
  },

  // CORRECT, but only if no normalize_string is defined in any base
  // class (see #evtconc):
  normalize_stringie: function (value) { return _.trim(value) },
})

from setoptpropag

ExamplePropagation of options (optpropag):
sqim.on('change_foo', function (now, old, options) {
  if (!options.noSync) {
    $.ajax({
      url: 'update',
      type: 'POST',
      data: this.get(),
    })
  }
})

// The handler above performs an AJAX request:
sqim.ifSet('foo', 123)

// But not now (set() passes options through to ifSet()):
sqim.set('foo', 123, {noSync: true})

// assignResp() passes options to set() so no request is performed too:
sqim.assignResp({foo: 123}, {noSync: true})

Defined in: main.js, line 5496

owned ( )

Part of: tag_Nesting

Called after this instance has been nest’ed into an _owning sqimitive (changed parents or got a first _parent).

By the time owned is called _parent and _parentKey of self are already set to new values.

See also unnest() that gets called before _parent is changed/removed.

Example
var MyChild = Sqimitive.Base.extend({
  events: {
    // Will append this.el to parent's .some-point node as soon as
    // this instance gets a new parent. If you want to do this in
    // production though, look for the attachPath _opt'ion of
    // Sqimitive\jQuery.
    owned: function () {
      this.attach(this._parent.$('.some-point'))
    },
  },
})

Other notes:

  • plainstubThis method returns nothing.
  • It should not be called directly.
  • It’s defined as stub in Base which lets Sqimitive remove this function instead of calling it as a handler doing nothing when a new handler is registered for this event (e.g. in a subclass via events as in the example above).

Defined in: main.js, line 6181

postInit ( opt )

Part of: tag_Lifecycle

Called after init() to bootstrap the new instance after constructor’ing.

Logically, init is part of the object construction while postInit is part of its lifecycle; while init is inseparable from new, postInit could be called at a later time (in theory) so it should not make the object inconsistent. Thinking about it that way helps to decide which of the two events to hook.

Usually init() creates and configures related objects (DOM nodes, collections, etc.) while postInit() starts timers, resource preloading, etc. This way you don’t depend on the order of init() handlers (it may so happen that the init handler of a subclass is executed before the inherited init of its base class, when internal objects are not yet initialized).

Other notes:

from initonce

Example
var MySqimitive = Sqimitive.Base.extend({
  _button: null,

  events: {
    init: function () {
      this._button = this.nest(new MyButton)
    },

    postInit: function () {
      this._button.startAnimation()
    },
  },
})

Defined in: main.js, line 5140

remove ( )

Part of: tag_Nesting, tag_Lifecycle

Removes el and calls unnest().

Result Types
Types Notes
baseRemoveobject this

rmvsunnUse unnest() when an object is temporary elided from the hierarchy (e.g. because it’s changing parents). Use remove() when it’s completely destroyed and its “view” (DOM node, etc.) is no longer needed. By convention, remove() is also used on el-less objects (where it’s identical to unnest() in effect) to convey the intention of destroying them.

Example
var child = new Sqimitive.Base({el: '<p>Some text.</p>'})
var parent = new Sqimitive.Base({el: '<article>'})
parent.nest(child).attach(parent.el)
  // now we have <article><p>Some text.</p></article>

child.unnest()
  // we still have <article><p>Some text.</p></article> even though
  // child is no more listed under parent._children

// But if we would have done this:
child.remove()
  // we would have <article></article> with the child removed from
  // both the parent's _children list and from its the parent's el

from elstub

Sqimitive\Base doesn’t define any element-related functionality but only provides stubs for several common fields (el, render(), remove(), etc.) for standardized extension. See Sqimitive\jQuery.el for one such subclass.

Base’s remove() simply calls unnest().

Defined in: main.js, lines 6655-6657 (3 lines) • Show code

remove: function () {
  return this.unnest()
},
render ( )

Part of: tag_Lifecycle

Populate from scratch or update the object’s “view” – el.

Result Types
Types Notes
renderAttachbaseAttachobject this

Here’s a typical Sqimitive object lifecycle:

  • construct with new: new Class({opt...})
  • attach() (to DOM, etc.), for members – when nested to a collection
  • render() when required for user to see it first time
  • render() again when something changes that affects the visual presentation (usually in response to a change of some _opt’ion)
  • finally, remove()

Complete re-rendering on change is simple and adequate for many simple sqimitives but if it’s heavy, it’s customary to perform a partial update using method(s) named update(). There are no such methods by default but see vw for an example.

Example
var Label = Sqimitive.jQuery.extend({
  _opt: {caption: 'untitled'},

  events: {
    render: function () {
      this.el.text(this.get('caption'))
    },

    change_caption: 'render',
  },
})
                                    // Lifecycle:
;(new Label)                        // 1) construct
  .attach('body')                   // 2) attach
  .render()                         // 3) render
  .set('caption', 'render again!')  // 4) update

from elstub

Sqimitive\Base doesn’t define any element-related functionality but only provides stubs for several common fields (el, render(), remove(), etc.) for standardized extension. See Sqimitive\jQuery.el for one such subclass.

Base’s render() calls attach() on all children of self (but not on self).

Defined in: main.js, lines 5234-5237 (4 lines) • Show code

render: function () {
  this.invoke('attach')
  return this
},
set ( opt, value[, options] )

Part of: tag_Options

Changes the value of one _opt’ion and returns this.

set() is identical to ifSet() except the latter returns true/false indicating if the new value was different from the old one or options.forceFire was set.

If you are overriding the “setter” behaviour you should override ifSet instead of set which calls the former.

Example
sqim.set('key', 'foo')     //=> this

// Fires normalize/change_key/change despite the fact that old key's
// value is the same:
sqim.set('key', 'foo', {forceFire: true})

The description of ifSet() follows.

from ifSetSet

Fires normalize_OPT giving it value; if the result is not isEqual to this._opt[opt] or if options.forceFire is set – changes _opt and fires batch’ed change_OPT event, then change.

ExampleYou can take advantage of ifSet()’s return value to perform interlocking operations saving a call to get():
if (sqim.ifSet('eventsBound', true)) {
  // eventsBound was previously not isEqual() and it was now changed to
  // true so we can do what we need, once until it changes to non-true
  // again.
}
…As a short form of writing:
if (!sqim.get('eventsBound')) {
  sqim.set('eventsBound', true)
  // ...
}

Other notes:

  • Avoid forceFire because it calls not only your object’s handlers (which you expect) but also other instances’ (which you usually don’t).
  • Use set() which returns this if you don’t care for the return value but want method chaining.
  • Use getSet() if you want to update options based on other options (e.g. increment a value).
  • Use batch() to assign related options and defer on-change behaviour.
  • There’s no set() version that writes multiple options at once. You might be looking for assignResp(), possibly with schema (useful when assigning an API response): assignResp(opts, {schema: {}}) - equivalent to batch’ed bunch of set()-s. Generally, consider assignResp() if you find yourself writing a series of set().
  • It is safe to change _opt from within normalize_OPT or change handlers – they are written to this._opt immediately but subsequent change_OPT and change events are batch’ed in FIFO fashion (first set – first fired). This preserves “incremental” update order but also means that set() is not always immediately followed by a call to these handlers.
  • Because ifSet() creates an implicit batch, change_OPT and change fired for OPT receive the same options.batchID even if you don’t wrap the ifSet() call into a batch().
  • getnsetget() and set() (ifSet()) may be seen as public or private API at user’s discretion. A complex class may opt for wrappers around these, discouraging its users from direct access to options:
    title: function () { return this.get('title') }
  • See also Options overview (opt).
ExampleOverriding ifSet() is possible but most of the time normalize_OPT is what you need:
var MySetter = Sqimitive.Base.extend({
  _opt: {
    readOnly: 'foo',
  },

  events: {
    // Our handler will be called before the inherited ifSet() and
    // will prevent modification of this._opt.readOnly when they are
    // done via set/ifSet (i.e. the recommended way).
    //
    // Make sure not to hook '-set' because set() calls ifSet() and it
    // would be still possible to change 'readOnly' with
    // ifSet('readOnly', 'bar').
    '-ifSet': function (opt) {
      if (opt == 'readOnly') {
        throw new Error('You shouldn\'t really change what is called readOnly, you know?')
      }
    },

    // normalize_OPT would be better though:
    normalize_readOnly: function () {
      throw new Error('You shouldn\'t change what is called readOnly, you know!')
    },
  },
})
Hooking ifSet() specifically allows immediate, not batch’ed reaction (but consider using priority^ – see evtref). options will lack some fields at this point though (_batchOptions()).
'-ifSet': function (opt, value, options) {
  console.log('Option ', opt, ' is about to change to ', value)
},

Defined in: main.js, lines 5489-5492 (4 lines) • Show code

set: function (opt, value, options) {
  this.ifSet(opt, value, options)
  return this
},
sink ( func, args, onSelf )

Part of: tag_Events

Calls the method func with arguments args on all nested _children (recursively) and optionally onSelf.

from bubsin

Result Types
Types Notes
this

If onSelf is true then first calls func on this, if defined.

bubble() proceeds upstream while sink() descends downstream. However, the first walks only _owning sqimitives (since it’s using _parent) while the second works for non-_owning too (since it’s using _children).

You can “recursively” fire events as well since events typically create methods (evt; see _wrapHandler()’s source code for details).

Note that it might get quite intense with heavy nesting.

Example
// Recursively causes all nested views and self to be rendered:
sqim.sink('render', []. true)

// Recursively calls remove() on self, all children and their
// children, removing every single sqimitive from its parent (useful
// when removing a parent el doesn't automatically free resources of
// its children like it does in DOM):
sqim.sink('invoke', ['remove'], true)

// We can use it to serialize the entire tree:
var serialized = []
sqim.sink('saveTo', [serialized])
localStorage.setItem('saved', JSON.stringify(serialized))
// Now if children implement something like this then serialized will
// contain a portable representation of the current hierarchy (without
// self):
child.on('saveTo', function (ser) { ser.push(this.get()) })

Defined in: main.js, lines 7340-7344 (5 lines) • Show code

sink: function (func, args, onSelf) {
  onSelf && this[func] && this[func].apply(this, args)
  this.invoke('sink', func, args, true)
  return this
},
slice ( [start [, end]] )

Part of: tag_Utilities

Treats this instance’s _children as an ordered array and returns a portion of it.

start defaults to 0, end – to length + 1.

Attention: the end index is not included into result. If start == end then an empty array is always returned.

Example
slice()       // get all children; toArray() does the same
slice(1, 2)   // get 2nd child as an array
slice(-1)     // get last child as an array; last() is more convenient
slice(5, 8)   // get 6th and 7th children as an array; no 8th!
slice(0, 0)   // start == end - always an empty array
slice(1, -1)  // get all children except the first and last

Along with length, slice() makes sqimitives look like an array and so slice()’s interface is the same as Array’s slice(): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice

from nestedslice

nested() without arguments returns an object – children with keys. slice() returns an array – children only, dropping their keys.

Unordered objects warning

from unordered

Attention: JavaScript objects are unordered, as explained in the description of Ordered.

slice() may return entries in arbitrary order.

It should not be used unless special measures were taken to make this instance properly ordered or if the caller doesn’t care for child order (but then it should never use a positive start as there are no guarantees which child will be at that index).

Use either the Ordered mixIn (and see at()) or override slice() to sort _children based on some criteria before taking a portion of it (this may be slower than Ordered that maintains sort order as children come and go without resorting on every call to slice()):

events: {
  '=slice': function (sup, start, end) {
    var sorter = function (a, b) { return a.get('pos') - b.get('pos') }
    return sup(this).sort(sorter).slice(start, end)
    // WRONG: do not use any #util functions to avoid recursion:
    return this.toArray()...
  }
}

Making slice() “ordered” is enough to make the Sqimitive ordered because slice is used by all other functions that treat _children as an array (each(), find() and others, see util).

Defined in: main.js, lines 6492-6499 (8 lines) • Show code

slice: function (start, end) {
  // _.values(), _.toArray(), Object.keys(), etc. return values in arbitrary
  // order. The Base Sqimitive is unordered.
  //
  // Warning: do not replace _.values() with this.toArray() since the latter
  // calls this.slice().
  return _.values(this._children).slice(start, end)
},
sort ( func [, cx] )

Part of: tag_Utilities

Returns _children as an array in order specified by comparison func.

Arguments
Name Types Notes
funcfunction Receives two members, must return -1/+1 if first member should go before/after second or 0 if order doesn’t matter (may cause non-deterministic result).
cxobject The context for func
null/omitted use this

This differs from standard Array’s sort() that works in-place and doesn’t accept cx.

Use sortBy() util to compare based on weight values returned by func.

Example
nest({foo: 5})
nest({foo: 1})
sort(function (a, b) { return a.get('foo') - b.get('foo') })
  //=> [ {foo: 1}, {foo: 5} ]
sortBy(function (a) { return a.get('foo') })
  //=> same order

Defined in: main.js, lines 6582-6584 (3 lines) • Show code

sort: function (func, cx) {
  return this.slice().sort(func.bind(cx || this))
},
unlist ( key )

Part of: tag_Nesting

Removes a child by its key or instance from own _children.

Result Types
Types Notes
object removed sqimitive
undefined nothing found
Arguments
Name Types Notes
keystring or number key which to clear
null to do nothing
object instance to remove as per findKey()
Example
collection.unlist('foo')    //=> Sqimitive or undefined
collection.unlist(collection.nested('foo'))   // identical to above
collection.unlist(child)    //=> Sqimitive (child) or undefined

If _owning is set (as it is by default) calls remove(), else deletes the key in _children and calls unnested() on self.

Does nothing if this key/child is not contained (returns undefined).

See also unnest() which is called on the child (not on the parent object). However, it’s only usable in _owning collections since there’s no reverse child → parent relationship in non-_owning mode.

Defined in: main.js, lines 6246-6266 (21 lines) • Show code

unlist: function (key) {
  if (key instanceof Object) {
    key = this.findKey(key)
  }

  if (key != null) {
    var sqim = this._children[key += '']
  }

  if (sqim) {
    if (this._owning) {
      sqim.remove()
    } else {
      delete this._children[key]
      --this.length
      this.unnested(sqim, key)
    }
  }

  return sqim
},
unnest ( )

Part of: tag_Nesting

Removes this instance from its _parent object, if any.

Result Types
Types Notes
this

unnest() does nothing if _parent of this is already null (i.e. not owned).

Note: it normally doesn’t remove own el from its parent node – use remove() for this.

from rmvsunn

Use unnest() when an object is temporary elided from the hierarchy (e.g. because it’s changing parents). Use remove() when it’s completely destroyed and its “view” (DOM node, etc.) is no longer needed. By convention, remove() is also used on el-less objects (where it’s identical to unnest() in effect) to convey the intention of destroying them.

Example
var child = new Sqimitive.Base({el: '<p>Some text.</p>'})
var parent = new Sqimitive.Base({el: '<article>'})
parent.nest(child).attach(parent.el)
  // now we have <article><p>Some text.</p></article>

child.unnest()
  // we still have <article><p>Some text.</p></article> even though
  // child is no more listed under parent._children

// But if we would have done this:
child.remove()
  // we would have <article></article> with the child removed from
  // both the parent's _children list and from its the parent's el
ExampleA child reacting to its unnesting:
var MyChild = Sqimitive.Base.extend({
  events: {
    '-unnest': function () {
      // this._parent and _parentKey are still set if this instance
      // was attached to any parent, otherwise they are null.
      this._parent && alert("I am about to be... unnested! :'(")
    },

    unnest: function () {
      // At this point _parent and _parentKey are certainly null but
      // there's no telling if they will remain so - or if this
      // instance had any parent before unnest() was called.
      alert('I am now as free as the wind!')
    },

    // In contrast to the above, here we can reliably determine if
    // this sqimitive was previously nested and if it was - do
    // something after it was unnested by calling the inherited
    // implementation via sup.
    '=unnest': function (sup) {
      var hadParent = this._parent != null
      sup(this, arguments)
      hadParent && alert('I was abducted but you saved me!')
      return res
    },
  },
})

Other notes:

Defined in: main.js, lines 6323-6333 (11 lines) • Show code

unnest: function () {
  var parent = this._parent
  if (parent) {
    var key = this._parentKey
    delete parent._children[key]
    this._parent = this._parentKey = null
    --parent.length
    parent.unnested(this, key)
  }
  return this
},
unnested ( sqim, key )

Part of: tag_Nesting

Called right after a child (sqim) was detached from its parent (this) where it was listed under key.

unnestedoffBase’s implementation of unnested() calls sqim.off(this) to unregister all event handlers that might have been previously attached to the removed child by this instance (provided they were not hardwired with fuse() and used cx === this).

By the time unnested is called, sqim’s _parent and _parentKey are already null.

Former key is useful for non-_owning collections, especially when it may have duplicates (same child object under different keys).

Example
var MyParent = Sqimitive.Base.extend({
  events: {
    unnested: function (sqim, key) {
      alert('Whence thou goeth, my ' + key + '...')
    },
  },
})

Other notes:

from plainstub

  • This method returns nothing.
  • It should not be called directly.

Defined in: main.js, lines 6371-6378 (8 lines) • Show code

unnested: function (sqim, key) {
  sqim.off(this)
  this._unnested(sqim, key)

  if (this.length < 0 && console && console.error) {
    console.error('Broken nesting: sqimitive.length below zero')
  }
},