A framework for single-page applications creation
<input class="in" type="text">
<output class="out"></output>

<script src="seemple.min.js"></script>
<script>
const app = new Seemple();
app.bindNode('x', '.in, .out');
app.x = 'A framework for single-page applications creation';
</script>

Download Github

Tutorials and examples

Some logos

Introduction

Features

  • Reactive API which allows to solve hard and confusing problems easily
  • High robustness of developed apps
  • The ability to refactor legacy applications without rewriting them from scratch
  • Only couple of hours is needed to master the framework because of an absence of complex concepts
  • One of the most user-friendly documentations among JavaScript libraries

Who needs such framework?

Seemple.js fills the gap between a junior and a senior

  • For those who is new at JavaScript, wishing to master the development of single-page applications
  • For full-stack devs, for whom front-end development is in the second place after the back-end
  • For all those who are not satisfied with the current order of things in the universe of web development

What's the business need solved by Seemple.js framework?

It is not a secret that the barrier to entry into the web development is becoming higher, the list of requirements to a developer is bigger, a number of dependencies in projects may be up to several hundred. Despite the fact that the studying of JavaScript is very popular today, the demand for developers who can accomplish tasks successfully is greater than the supply.

Due to extreme simplicity of the framework, even novice web developers can quickly start to do small, then medium and then large web applications. This means that web studios are able to save money by hiring younger professionals, who, in turn, could not find a job before.

It is only for beginners?

Two things that present Seemple.js as a simple framework are use of selectors of bound DOM nodes when declaring a two-way data binding and an absence of any restrictions on requirements of an architecture and design patterns. In other aspects Seemple.js is a modern general-purpose framework developed using today's technologies. At least, Seemple.js may cause an interest because it uses the full potential of getters and setters in JavaScript.

Website instructions

Importance levels

The API is split up into three parts.

The first level - the most important stuff

After you've learned the most important API parts you can hardily start doing fantastic things. Classes, methods and properties marked with need to be learned primarily.

If you already know the quick-start basics, you can look at less important (but still important) methods and properties of the framework.

The third level - other methods and properties

If you want to know everything about Seemple.js, turn on "Advanced mode" checkbox from the menu.

Warning. If you open a link to a method or a property of the third level of importance, the "Advanced mode" is turned on automatically.

Inaccuracies and typos

In a footer of every article of the documentation you can find a link to a source of that article. You can fix a typo directly at Github editor. If you cannot do this somehow, then select the text of the typo and press CTRL + Enter to send a message to the developer.

The documentation is written using JSDoc3 and GitHub Flavored Markdown.

Modules

If you use CommonJS you can see paths to modules at every article of the documentation. A size of resulting JavaScript bundle can be reduced by the import of needed parts of the framework.

// every static function or a class can be imported as CJS module
const SeempleArray = require('seemple/array');
const propBinder = require('seemple/binders/prop');
const bindNode = require('seemple/bindnode');

The import of the main module pulls entire framework. Usually it's not required.

const Seemple = require('seemple');

A pure module of Seemple class (without binders, Array and Object) lives at 'seemple/seemple'

Examples

Hello World!

Writing your first application is very easy. You should:

1. Create an HTML file with the following content

<!DOCTYPE html>
<html>
    <head>
        <title>My first Seemple.js application</title>
    </head>
    <body>
        <input type="text" class="my-input">
        <div class="my-output"></div>
        <script
          src="https://finom.github.io/seemple/seemple.min.js">
        </script>
        <script src="js/app.js"></script>
    </body>
</html>

2. Create the application class at js/app.js

// store html binder in a short variable
const htmlBinder = Seemple.binders.html;

// create the class which inherits Seemple
class Application extends Seemple {
    constructor() {
        super();

        // bind a property x and the text field
        this.bindNode('x', '.my-input');

        // bind the property x and the ".my-output" block
        this.bindNode('x', '.my-output', htmlBinder());

        // if the property "х" has changed,
        // inform about it in the console
        this.on('change:x', () =>
            console.log(`x is changed to "${this.x}"`));
    }
}

const app = new Application();

3. That's it!

Now you can open the developer's console (by pressing F12) and write:

app.x = 'Hello World!';

Cool, isn't it? You can work with such magical properties directly.

1. TodoMVC - a to-do list. (Source code with annotations)

2. TreeView of unlimited nesting depth.

3. Markdown editor made with few lines of code.

4. Simple SoundCloud player - music search via SoundCloud API.

5. Contact List - allows to add, remove, sort, search and change contacts.

Class Seemple

CommonJS module: 'seemple/seemple'

The class Seemple is a core of the Seemple.js framework which is inherited by Seemple.Array, Seemple.Object (and every class of an application you create). It contains the main functionality of the framework: mediators, dependencies, two-way data binding, an event engine, etc.

This class usually isn't used directly. Instead, it is inherited by other classes.

Links

Examples

Creating of an instance

const seemple = new Seemple();

Inheritance

class MyClass extends Seemple {
	constructor() {
		this.sayHello();
	}
	sayHello() {
		alert("Hello World!");
	}
}

Inheritance using Seemple.Class

const MyClass = Seemple.Class({
	'extends': Seemple,
	constructor() {
		this.sayHello();
	},
	sayHello() {
		alert("Hello World!");
	}
});

Seemple#bindNode(key, node, binder, eventOptions) object

Binds a property of an object to HTML node, implementing two-way data binding

Seemple#bindNode is the only method of the Seemple class which is responsible for changing DOM (except array renderer). It creates a bridge between value of a property and state of HTML node on the page: from a simple input to a complicated widget (the complexity of elements is unlimited). After using this method, it isn't necessary to monitor the synchronizations between model and view.

Note that the method has static alternative, which works just the same but accepts any object as the first argument, shifting rest arguments to the right.

const bindNode = require('seemple/bindnode');
const object = {};
bindNode(object, key, node, binder, eventOptions);
// instead of this.bindNode(key, node, binder, eventOptions);

The method acepts three arguments: a property name, HTML node and a binding rule (a binder). In its turn, a binder is an ordinary object and it can have the following properties: on, getValue, setValue, initialize, destroy (Read more here: binder). All the five properties are optional. It also allows to declare one-way data bindings (any direction).

The bindNode method supports the many-to-many bindings. Several elements can be bound to one property and several properties can be bound to one element, including ones from different instances of various classes.

this.bindNode('myKey', '.my-element', {
    on: 'click',
    getValue() { ... },
    setValue() { ... }
});

For example, you want to bind a property of an object to a input[type="checkbox"] node:

this.bindNode('myKey', '.my-checkbox', {
    // when is element state changed?
    // - after 'click' event
    on: 'click',
    // how to extract element state?
    // - return 'checked' value
    getValue() {
        return this.checked;
    },
    // how to set element state?
    // - set 'checked' value
    setValue(v) {
        this.checked = !!v;
    }
});

After binding is declared, you can set value of an object property in your most habitual way and HTML node (in this case, a checkbox) will change its state immediately. After clicking on the checkbox, the property value will be changed to the corresponding one as well.

// sets checked = true
this.myKey = true;

More complicated example: binding object property to jQuery UI widget

<div class="my-slider"></div>
this.bindNode('myKey', '.my-slider', {
    // when is element state changed?
    // - after 'slide' event
    on: 'slide',
    // how to extract element state?
    // - return 'value' of the widget
    getValue() {
        return $(this).slider('option', 'value');
    },
    // how to set element state?
    // - set 'value'
    setValue(v) {
        $(this).slider('option', 'value', v);
    },
    // how to initialize the widget?
    // you can initialize the slider in any way,
    // but 'initialize' function provides some syntactic sugar
    initialize() {
        $(this).slider({ min: 0, max: 100 });
    }
});
// will set the slider value 42
this.myKey = 42;

It looks easy but you may ask a question: "What should I do to avoid writing these rules every time?". Indeed, there can be a lot of elements of the same type on the page: text fields, drop down menus, fields from the HTML5 specification as well as third party widgets (see the example above).

As observed in this documentation, the third argument is not obligatory for the ones of the bindNode method (see below). This problem is solved by the Seemple.defaultBinders array which contains functions checking an HTML node against a set of rules and returns corresponding binder or undefined. You get an opportunity to reduce your code a great deal, putting binding rules into a separate part of your code and to use a syntax for binding without the third argument:

this.bindNode('myKey', '.my-element');

How to do it? You should add a function checking your element against a set of rules to the beginning of the Seemple.defaultBinders array.

const checkboxBinder = () => {
    return {
        on: 'click',
        getValue() {
            return this.checked;
        },
        setValue(v) {
            this.checked = !!v;
        }
    }
};

// the unshift method adds the function
// to the beginning of the Seemple.defaultBinders array
Seemple.defaultBinders.unshift(node => {
    // check if the element is a checkbox
    if(node.tagName == 'INPUT' && node.type == 'checkbox') {
        // if checking is OK, return a new binder
        return checkboxBinder();
    }
});
this.bindNode('myKey', '.my-checkbox');
this.myKey = true;

What should you do if you need to pass arguments for initializing some plugin or a widget? You can call the function that returns a binder manually.

const uiSlider = (min, max) => {
    return {
        on: 'slide',
        getValue() {
            return $(this).slider('option', 'value');
        },
        setValue(v) {
            $(this).slider('option', 'value', v);
        },
        initialize() {
            $(this).slider({ min: min, max: max });
        }
    }
};
this.bindNode('myKey1', '.my-slider1', uiSlider(0, 100));
this.bindNode('myKey2', '.my-slider2', uiSlider(1, 1000));
this.myKey1 = 42;
this.myKey2 = 999;

For global access to the binder you can add new property to Seemple.binders.

Seemple.binders.uiSlider = uiSlider;
// ...
this.bindNode('myKey1', '.my-slider1', Seemple.binders.uiSlider(0, 100));
this.bindNode('myKey2', '.my-slider2', Seemple.binders.uiSlider(1, 1000));

Seemple.defaultBinders OOB has a support for all form elements without any exception: select (including multiple), textarea, output, input (including all types from the specification of HTML5: text, checkbox, radio, range, number, date, search, time, datetime, datetime-local, color and others). That means it is not necessary to designate a binder for standard elements.

<input type="color" class="my-color-input">
this.bindNode('myColor', '.my-color-input');
this.myColor = '#66bb6a';

Besides, after the binding, a new non-standard :bound(KEY) CSS selector is available for you.

this.bindNode('myKey', '.my-element');

// will find the element '.my-inner-element' inside '.my-element'
this.bindNode('myAnotherKey', ':bound(myKey) .my-inner-element');

And the syntax of possible event names is extended:

this.bindNode('myKey', '.my-element');

// will handle the click on the '.my-element' element
this.on('click::myKey', () => { ... });

// will handle the click on the '.my-element .my-inner-element'
this.on('click::myKey(.my-inner-element)', () => { ... });

If a node is not found "Bound element is missing" error will be thrown. Check out Seemple#bindOptionalNode.

Sandbox definition

Seemple#bindNode can associate a class instance with the "main" HTML element on the page creating so-called sandbox. It is necessary to limit the instance influence on other HTML nodes. A special property sandbox is used for binding a sandbox.

<div class="my-sandbox">
    <!-- your HTML code -->
</div>
this.bindNode('sandbox', '.my-sandbox');

The definition of the sandbox adds lots of conveniences for you. For example:

Keep in mind that you can bind only one sandbox element to sandbox property. You can try Seemple#bindSandbox which does the same thing but removes previous binding.

// declare a sandbox
this.bindNode('sandbox', '.my-sandbox');

// .my-element is being searched for in the sandbox
this.bindNode('myKey', ':sandbox .my-element');

// it is not required to specify a key
// for the delegated events inside a sandbox
this.on('click::(.my-button)', () => { ... });

// will put the .inner-node element
// which is inside the sandbox into the console
console.log(this.$('.inner-node'));

Important features of the method and special flags

The fourth argument of bindNode method is eventOptions. As usual this object can include special flags or custom data which will be passed to bind and bind:KEY event handlers.

this.on('bind:x', evt => {
    console.log(evt.foo); // bar
});
this.bindNode('x', node, binder, { foo: 'bar' });

To understand important features of bindNode it is required to read information below but it's not required to remember all these flags.

A flag exactKey=false

If key string includes a dot then such string will be interpreted as a path to a property of nested object. Seemple will listen all changes of given object tree.

this.a = { b: { c: 'foo' } };
this.bindNode('a.b.c', node);

this.a.b.c = 'bar'; // updates node by bar

const oldB = this.a.b;

this.a.b = { c: 'baz' }; // updates node by baz

// node is not updated because
// the connection with the object subtree is destroyed
oldB.c = 'fuu';

In case if you need to use property name as is, use exactKey flag with true value.

this['a.b.c'] = 'foo';
this.bindNode('a.b.c', node, binder, {
    exactKey: true
});
this['a.b.c'] = 'bar';

A flag getValueOnBind

When getValue is given then a state of an element will be extracted and assigned to bound property immediately after bindNode call in case if the property has undefined value. To force this behavior even if the property has non-undefined value use getValueOnbind flag with true value. To cancel this behavior use the same flag with false value.

A flag setValueOnBind

When setValue is given then the value of the property will be set as element state immediately after bindNode call in case if the property has non-undefined value. To force this behavior even if the property is undefined use setValueOnBind flag with true value. To cancel this behavior use the same flag but with false value.

Flags debounceGetValue=true and debounceSetValue=true

One of the most important feature of bindNode is that the logic of property change and the logic of element state change uses debounce pattern. It means that if bound property is changed many times in a short time then bound element state will be updated only once after small delay (thanks to debounceSetValue=true). If element state is changed many times in a short time (eg corresponding DOM event is triggered), the property gets new value only once after minimum delay (thanks to debounceGetValue=true).

const input = document.querySelector('.my-input');
this.bindNode('x', input);
this.x = 'foo';
console.log(input.value === 'foo'); // false
setTimeout(() => {
    console.log(input.value === 'foo'); // true
});

To cancel this behavior (e. g. initiate synchronous binding) use debounceSetValue and/or debounceGetValue flags with false value.

Flags debounceSetValueOnBind=false and debounceGetValueOnBind=false

As described above bindNode uses debounce pattern on property change and on bound node change. This doesn't apply to a moment when bindNode is called. To remind, when the method is called a property or a node is changed immediately. When debounceGetValueOnBind and/or debounceSetValueOnBind are set to true then debounce is turned on for binding initialization as well.

Flags debounceSetValueDelay=0 и debounceGetValueDelay=0

These flags allow to set debounce delay. debounceSetValueDelay is used when debounceSetValue or debounceSetValueOnBind is true, debounceGetValueDelay is used when debounceGetValue or debounceGetValueOnBind is true.

A flag useExactBinder=false

Even if you pass a binder to bindNode, the framework tries to find default binder at Seemple.defaultBinder and extend it by properties of the passed object. This feature makes possible to use partially re-defined default binder.

For example, we want to bind input[type="text"] to a property. By default, the standard binder contains "on" property with "input" value for this kind of node. It means that the value of the instance property and node state will be synchronized when a user releases a key of the keyboard or pastes text from clipboard. In case if you want synchronization to be performed after the "blur" DOM event, you need to pass an object containing the only "on" property as the third argument. This object will extend the default binder, having retained getValue and setValue values.

this.bindNode('myKey', '.my-input', { on: 'blur' });

To cancel this behavior and use the binder as is, you can use useExactBinder flag with true value.

this.bindNode('x', node, binder, {
    useExactBinder: true
});

Returns object - self

Fires bind bind:KEY

Arguments

Name Type Details
key string seemple

A property name

node string node $nodes

An HTML element which must be bound to a key

binder optional binder

A binder containing the following properties: on , getValue, setValue, initialize, destroy. You can get more detailed information about binders in their documentation: see binder

eventOptions optional eventOptions

An event options which accepts "silent" (don't fire "bind" and "bind:KEY"), flags described above or custom data

Links

Seemple#bindNode(bindings, binder, eventOptions) object

Alternative syntax: passing of an object

To the Seemple#bindNode method an object can be passed to avoid multiple invocation of the method and reduce code. Keys of the object are property names and values can get the following look:

  • A node
  • An object with properties node and binder
  • An array of objects with properties node and binder

If binder arg is passed as the second argument then it wil be used as the binder for those elements for which a binder wasn't specified.

Returns object - self

Arguments

Name Type Details
bindings object

(see the example)

binder optional binder

(see above)

eventOptions optional eventOptions

(see above)

Examples

this.bindNode({
	foo: '.custom-checkbox',
	'bar.length': 'textarea'
});
this.bindNode({
	foo: {
		node: ':sandbox .aaa',
		binder: Seemple.binders.html()
	},
	bar: '.bbb',
	baz: [{
		node: '.ccc'
	}, {
		node: document.querySelector('.ddd'),
		binder: Seemple.binders.prop('baz')
	}]
}, {
	// will be used as a binder for .bbb and .ccc
	setValue(value) {
		foo(value);
	}
});

Seemple#bindNode(batch, commonEventOptions) object

Alternative syntax which makes possible to define unlimited amount of bindings per one bindNode call.

The method variation allows to pass an array of objects which need to include the following properties:

  • key - a property name
  • node - an HTML element which must be bound to a key
  • binder - a binder (optional)
  • event - event options (optional)

The second arg object includes common event options for all bindings and extends event object (properties from event has more priority).

Returns object - self

Arguments

Name Type Details
batch array

A batch of bindings

commonEventOptions optional eventOptions

Common event options

Examples

this.bindNode([{
	key: 'a',
	node: '.my-node',
	binder: {
		setValue(v) {
			doSomething(v);
		}
	}
}, {
	key: 'b',
	node: document.querySelectorAll('.bar')
	event: {
		foo: 'bar'
	}
}, {
	key: 'c.d.e',
	node: jQuery('.baz'),
	binder: Seemple.binders.html(),
	event: {
		silent: true,
		exactKey: true
	}
}], {
	getValueOnBind: false
});

Seemple#calc(target, source, handler=(v)=>v, eventOptions)

Creates a dependency of one property value on values of others

calc creates a dependency of a property (target argument) on values of other properties (source argument). When source property is changed, target is re-calculated automatically.

Note that the method has static alternative, which works just the same but accepts any target object as the first argument, shifting rest arguments to the right.

const calc = require('seemple/calc');
const object = {};
calc(object, target, source, handler, eventOptions);
// instead of this.calc(target, source, handler, eventOptions);

source arg has few variations.

A string

A target property is dependent on source property.

this.b = 1;
this.calc('a', 'b', b => b * 2);
console.log(this.a); // 2

An array of strings

A target is dependent on properties listed at source array.

this.b = 1;
this.c = 2;
this.d = 3;
this.calc('a', ['b', 'c', 'd'], (b, c, d) => b + c + d);
console.log(this.a); // 6

An object with properties object and key

At this case target property is dependent on a property from another object.

const someObject = { b: 1 };
this.calc('a', {
    object: someObject,
    key: 'b'
}, b => b * 2);

console.log(this.a); // 2

key property also accepts an array of property names.

const someObject = {
    b: 1,
    c: 2,
    d: 3
};
this.calc('a', {
    object: someObject,
    key: ['b', 'c', 'd']
}, (b, c, d) => b + c + d);

console.log(this.a); // 6

An array of object with properties object и key

This variation allows to define dependency from properties of different objects.

const someObjectX = {
    b: 1,
    c: 2
};
const someObjectY = {
    d: 3
};
this.calc('a', [{
    object: someObjectX,
    key: ['b', 'c']
}, {
    object: someObjectY,
    key: 'd'
}], (b, c, d) => b + c + d);

console.log(this.a); // 6

A combination of strings (own properties) and objects

this.b = 1;
this.c = 2;

const someObject = {
    d: 3,
    e: 4
};

this.calc('a', ['b', 'c', {
    object: someObjectX,
    key: ['d', 'e']
}], (b, c, d, e) => b + c + d + e);

console.log(this.a); // 10

For reasons of code purity, the combination of strings and objects inside source array is not recommended. Instead, pass an object whose object property refers to source object. An example below makes the same job as shown at the previous example.

this.b = 1;
this.c = 2;

const someObject = {
    d: 3,
    e: 4
};

this.calc('a', [{
    object: this, // the target object is "this"
    keys: ['b', 'c']
}, {
    object: someObjectX,
    key: ['d', 'e']
}], (b, c, d, e) => b + c + d + e);

console.log(this.a); // 10

A path to source property

If source property name includes a dot then the method initiates a dependency on a property from nested object.

this.b = { c: { d: 1 } };
this.e = { f: { g: 2 } };

this.calc('a', ['b.c.d', 'e.f.g'], (d, g) => d + g);

console.log(this.a); // 3

The same thing works for external sources.

this.b = { c: { d: 1 } };
const someObject = { e: { f: { g: 2 } } };

this.calc('a', [{
    object: this
    key: 'b.c.d'
}, {
    object: someObject
    key; 'e.f.g'
}], (d, g) => d + g);

console.log(this.a); // 3

The method is protected from circular references (for example, a depends on b, b depends on c and c depends on a) and if there is a calculation error, it does not block the page and does not throw an exception about the stack over-flow.

As you may noticed, arguments of handler function always follow the same order as source properties appear.

In case if you want to change a value of one source property and make it so that target property will not be recalculated, then use Seemple#set method with skipCalc flag.

this.calc('a', 'b', handler);
this.set('b', newValue, {
    skipCalc: true
});

Important features of the method and special flags

The fourth argument of calc method is eventOptions. As usual this object can include special flags or custom data which will be passed to change:TARGET event handler.

this.on('change:a', evt => {
    console.log(evt.foo); // 'bar'
});

this.calc('a', source, handler, { foo: 'bar' });

A flag debounceCalc=true

After calc is called, target property is calculated with no delays. But when source property is changed the debounce pattern is used. That means that target property will be changed in few milliseconds and only once even if source properties was changed many times in a short time.

this.b = 1;
this.c = 2;
this.d = 3;

this.calc('a', ['b', 'c', 'd'], (b, c, d) => b + c + d);

this.on('change:a', () => {
    // the handler will be called only once
    // despite that source properties was changed thrice
    console.log(`a is changed to ${this.a}`); // a is changed to 60
});

this.b = 10;
this.c = 20;
this.d = 30;
console.log(this.a); // 6 instead of 60

To cancel debounce pattern when source properties are changed, in other words to make the calculation synchronously (as it was in previous versions of the method) pass debounceCalc with false value to the method.

this.b = 1;
this.c = 2;
this.d = 3;

this.calc('a', ['b', 'c', 'd'], (b, c, d) => b + c + d, {
    debounceCalc: false
});

this.on('change:a', () => {
    // the handler will be called thrice
    // every time when b, c or d are changed

    // a is changed to... 15, 33, 60
    console.log(`a is changed to ${this.a}`);
});

this.b = 10;
this.c = 20;
this.d = 30;
console.log(this.a); // 60

A flag debounceCalcOnInit=false

As described above, target property is calculated immediately after the calc is called. To turn on debounce on calc call pass debounceCalcOnInit with true value to the method.

this.on('change:a', () => {
    // the handler will be called only once in a moment
    console.log(`a is changed to ${this.a}`); // a is changed to 6
});

this.b = 1;
this.c = 2;
this.d = 3;

this.calc('a', ['b', 'c', 'd'], (b, c, d) => b + c + d, {
    debounceCalcOnInit: true
});

console.log(this.a); // undefined

In real world debounceCalcOnInit flag is unlikely to be useful. Just keep in mind that you can enable "total debounce" if needed.

A flag debounceCalcDelay=0

The flag can be used to set debounce delay when debounceCalc or debounceCalcOnInit is set as true.

A flag setOnInit=true

It is known that target property gets new value after calc is called. To cancel this behavior and don't calculate a property immediately use setOnInit with false value.

this.calc('a', 'b', b => b * 2, {
    setOnInit: false
});

console.log(this.a); // undefined

// but if this.b is changed the target property will be calculated
this.b = 1;

A flag exactKey=false

As described above, it's possible to use a path to source property using a string that contains dots. In case if you need to use exact name of source property use exactKey with true value.

this['foo.bar.baz'] = 1;
this.calc('a', 'foo.bar.baz', fooBarBaz => fooBarBaz * 2, {
    exactKey: true
});
console.log(this.a); // 2

A flag promiseCalc=false

This flag allows to return Promise instance from the calculating function. Target property gets its value from resolved promise.

Warning! Promise cannot be canceled. Use the promiseCalc feature carefully and don't allow multiple calls of heavy functions.

this.calc('a', ['b', 'c'], (b, c) => {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve(a + b)
        }, 1000);
    });
}, {
    promiseCalc: true
});

this.b = 1;
this.c = 2;

// "a" will be changed in a second
this.calc('response', 'data', async (data) => {
    const resp = await fetch(url, {
        method: 'post',
        body: data
    });

    return resp.json();
}, {
    promiseCalc: true
});

Arguments

Name Default Type Details
target string

A property which needs to be calculated

source string array

Which properties the target property is depended on

handler optional (v)=>v function

A function which returns a new value

eventOptions optional eventOptions

An object which can contain some special flags or data for change:KEY handler (see above)

Examples

this.calc('greeting', 'name', name => `Hello, ${name}!`);

this.name = 'World';

// ... in a moment
alert(this.greeting); // 'Hello, World!'

The calculation of the rectangle perimeter with two sides known (and the calculation of the sides with the perimeter known)

this.a = 3;
this.b = 4;

this
    .calc('p', ['a', 'b'], (a, b) => (a + b) * 2)
    .calc('a', ['p', 'b'], (p, b) => p/2 - b)
    .calc('b', ['p', 'a'], (p, a) => p/2 - a);

alert(this.p); // 14

this.on('change:p', () => {
    // "The perimeter has been changed and equals 18"
    console.log(`The perimeter has been changed and equals ${this.p}`);
});

this.a = 5;

Seemple#calc(batch, commonEventOptions)

Extra syntax for Seemple#calc. Allows to define few calculated properties per single call of the method.

The first argument is an object whose keys are property names and values are objects with the following data:

  • source - which properties the target property is depended on;
  • handler - a function which returns a new value of a property (by default it equals to (value) => value);
  • event - event options.

The second argument contains common event options which extend event of every item (properties of event have higher priority).

source can take any kind of look as described above (a string, an array of strings etc).

Arguments

Name Type Details
batch array

An object which includes all information about calculated properties

commonEventOptions optional eventOptions

Event options which are common for all listed calculated properties

Examples

this.calc({
	x: {
    	source: ['a', 'b'],
    	handler: (a, b) => a + b
	},
	y: {
	    source: {
	        object: someObject,
	        key: 'c'
	    },
	    event: {
	        setOnInit: false
	    }
	},
	z: {
	    source: [{
	        object: this,
	        key: 'x'
	    }, {
	        object: someObject,
	        key: 'd'
	    }],
	    handler: (x, d) => x + d
	}
}, {
    debounceCalc: false
});

Seemple#instantiate(key, class, updateCallback) object

Creates fixed class instance

Note that the method has static alternative.

The method creates and fixes an instance of a class as given property value. When you try to override the property, instead of actual overriding, the instance is updated. Thus, we achieve integrity of an application, made on with Seemple.js.

The method is an addon over Seemple#mediate and it overrides a mediator.

Class instance is being created during the call of instantiate method. The current property value becomes the first argument of the class constructor. Provisions should be made so that either undefined (if a property hasn't contained any data before), or an object you should do something with (for example, to extend class instance by object properties) will get into the class constructor.

It looks easy in practice: you create an ordinary class which almost always receives some data to be handled (for example, to use it at Seemple.Object#setData method).

While attempting to assign another value (an object or an array) to the property, the inner mechanism of instantiate method performs the following instead of the actual assignment:

  • If updateCallback function is given, the method calls it with two arguments: current property value and the data which code is trying to assign.
  • If given class is inherited from Seemple.Object, the instance is updated with new data using Seemple.Object#setData method.
  • If given class is inherited from Seemple.Array, the instance is updated with new data using Seemple.Array#recreate method.
  • If updateCallback function isn't given and the class isn’t inherited from Seemple.Object or Seemple.Array, the instance is extended by object properties which is trying to be assigned.

A feature of this method is an absence of limitations on the class source. Any function-constructor that can be initialized using new operator and not only Seemple's successors can act as a class.

Returns object - self

Arguments

Name Type Details
key string array

A key or an array of keys

class function

A class whose instance becomes the property value

updateCallback optional function

A function which is called at every attempt of assigning new data to the property, allowing to customize logic of class instance updating with new data. The function receives two arguments: the current property value (class instance) and data which are attempted to assign.

Examples

class MyClass {
	// ...
}

// ...

this.instantiate('x', MyClass);

// trying to assign another value to x property
this.x = { a: 42 };

// this.x is still MyClass instance
alert(this.x instanceof MyClass); // true
alert(this.x.a); // 42

The use of updateCallback.

this.instantiate('x', MyClass, (instance, data) => {
	updateSomeHow(instance, data);
});

Getting a parent and property name. Besides data (the first argument), two arguments are passed to class constructor: a reference to an instance which has called instantiate and a name of a property.

class MyClass extends Seemple {
	constructor(data, parent, key) {
		// parent is MyParentClass instance
        // which is created x property
		// key equals to "x"
	}
}

class MyParentClass extends Seemple {
	constructor() {
		super().instantiate('x', MyClass);
	}
});

A non-standard way of using updateCallback for ignoring any changes of a property.

this.instantiate('x', MyClass, () => {});

In case if your class doesn't support the use of new operator, use Seemple#mediate method instead of instantiate.

this.mediate('x', (data, currentValue) => {
	return currentValue instanceof SomeClass
		? Object.assign(currentValue, data)
		: SomeLib.initInstance(SomeClass, data);
});

An abstract example with nested data (for brevity, "class instance fields" syntax is used)

// app.js
class App extends Seemple {
	constructor(appData) {
		this.appData = appData;
		this.instantiate('appData', AppData);
	}
}

// app-data.js
class AppData extends Seemple.Object {
	constructor(data) {
		super(data)
			.instantiate({
				friends: Friends,
				settins: Settings
			});
	}
}

// friend.js
class Friend extends Seemple.Object {
	constructor(data) {
		super(data);
	}
}

// friends.js
class Friends extends Seemple.Array {
	get Model() { return Friend; }
	trackBy = 'id';
	constructor(data) {
		super(...data);
	}
}

// settings.js
class Settings extends Seemple.Object {
	constructor(data) {
		super(data)
			.instantiate('credentials', Credentials);
	}
}

// credentials.js
class Credentials extends Seemple.Object {
	constructor(data) {
		super(data);
	}
}

// app-init.js
var app = new App({
	settings: {
		name: 'Vasiliy Vasiliev',
		credentials: {
			email: 'vasia.vasia@gmail.com'
		}
	},
	friends: [{
		name: 'Yulia Zuyeva',
		id: 1
	}, {
		name: 'Konstantin Konstantinopolsky',
		id: 2
	}, {
		name: 'nagibator3000',
		id: 3
	}]
});

// data can be serialized and passed to a server
JSON.stringify(app.appData);

// next just to assign new data to appData property
// yet the object structure won’t be changed
app.appData = {
	settings: {
		name: 'Petr Petrov',
		credentials: {
			email: 'petr.petrov@gmail.com'
		}
	},
	friends: [{
		name: 'Yulechka Zuyeva',
		id: 1
	}, {
		name: 'Konstantin Konstantinopolsky',
		id: 2
	}]
};

Seemple#instantiate(keyClassPairs, updateCallback) object

Additional syntax for Seemple#instantiate, which accepts key-class object as the first argument.

Returns object - self

Arguments

Name Type Details
keyClassPairs object

key-class object

updateCallback optional function

A function which is called at every attempt of assigning new data to a property

Examples

this.instantiate({
	x: Class1,
	y: Class2,
	z: Class3
}, (instance, data) => {
	instance.doSomethingWith(data);
});

Seemple#mediate(key, mediator) object

Transforms property value on its changing

This method is used for transforming property value on its changing. For example, you want the property value to be always either of a certain type or an integer value, or to be no less than zero and no more than a hundred etc.

Note that the method has static alternative, which works just the same but accepts any target object as the first argument, shifting rest arguments to the right.

const mediate = require('seemple/mediate');
const object = {};
mediate(object, key, mediator);
// instead of this.mediate(key, mediator);

Returns object - self

Arguments

Name Type Details
key string array

A key or an array of keys

mediator function

A function-mediator which returns a new value. It gets the following arguments: new value, previous value, a key, an object itself

Examples

this.mediate('x', value => String(value));

this.x = 1;

alert(typeof this.x); // "string"

An array of keys

this.mediate(['x', 'y'], value => String(value));

Seemple#mediate(keyMediatorPairs)

Alternative syntax of the Seemple#mediate method which accepts "key-mediator" object as an argument

Arguments

Name Type Details
keyMediatorPairs object

An object with key-mediator properties

Examples

this.mediate({
	x: String,
	y: Number,
	z: Boolean
});
this.x = 1;
this.y = 2;
this.z = 3;
alert(typeof this.x); // "string"
alert(typeof this.y); // "number"
alert(typeof this.z); // "boolean"

Seemple#off(names, callback, context) object

Deletes an event handler

It deletes a handler which has been created before. All the three arguments are optional. You can delete both all the events (without passing any argument) and separate ones (having passed only the event name, the event name and the handler, the event name and the handler and the context).

Returns object - self

Fires removeevent removeevent:NAME

Arguments

Name Type Details
names optional eventNames

A list of event names which are separated by spaces (for example, "change:x ajaxcomplete change:y")

callback optional eventHandler

A function-handler

context optional object

A context

Links

Examples

this.off('change:x bind');

The deletion of all events

this.off();

The deletion of an event with definite handler

const handler = function() {
	//...
}
this.on('change:x', handler);
this.off('change:x', handler);

The deletion of an event with definite context

const object = {};
this.on('change:x', handler, object);
this.off('change:x', handler, object);

Seemple#on(names, callback, triggerOnInit, context) object

Adds an event handler

The method adds an event handler for an instance of the Seemple class. Refer to the complete list of possible events with the description here: eventNames.

Note that the method has static alternative, which works just the same but accepts any target object as the first argument, shifting rest arguments to the right.

const on = require('seemple/on');
const object = {};
on(object, names, callback, triggerOnInit, context);
// instead of this.on(names, callback, triggerOnInit, context);

Returns object - self

Fires addevent addevent:NAME

Arguments

Name Type Details
names eventNames

An event name or some names which are separated by a space (for example, "change:x ajaxcomplete change:y")

callback eventHandler

A function which is caused by the event

triggerOnInit optional boolean

If triggerOnInit argument equals true, the handler will be called immediately after event initialization.

context optional object

A context of the handler. In other words, this when called callback

Links

Examples

this.on('foo', () => {
	alert('Custom Event is fired');
});

this.trigger('foo');

Passing a context

this.on('foo', function() {
	alert(this.a); // 5
}, { a: 5 });

this.trigger('foo', 'Hello world');

Calling a handler immediately after event initialization

// Displays "bar" at once and waits for a firing of "foo" event
this.on('foo', () => {
	alert('bar');
}, true);

Seemple#on(evtnameHandlerObject, triggerOnInit, context) object

Alternative syntax: "eventname-handler" pairs

In the Seemple#on method the object with the key-element pairs can be passed to avoid multiple invocation of the method and reduce your code.

Returns object - self

Arguments

Name Type Details
evtnameHandlerObject object

An object where keys are event names and values are event handlers

triggerOnInit optional boolean

If triggerOnInit argument equals true, all handlers will be called immediately after event initialization.

context optional object

A context of the handler

Examples

this.on({
	'custom': evt => ...,
	'click::x': evt => ...,
	'change:y': evt => ...,
});

Seemple#onDebounce(names, callback, debounceDelay, triggerOnInit, context) object

Adds an event handler which is called only once during a definite period of time

The method allows to add an event handler to an instance of the Seemple class, debouncing the handler. Callback function can be called only once during a definite period of time. As to the rest the method works the same as Seemple#on.

The method has static alternative.

Returns object - self

Fires addevent addevent:NAME

Arguments

Name Type Details
names eventNames

An event name or some names which are separated by a space (for example, "change:x ajaxcomplete change:y").

callback eventHandler

A handler which is caused by an event

debounceDelay optional number

A delay

triggerOnInit optional boolean

If triggerOnInit argument equals true, the handler will be called immediately after event initialization.

context optional object

A context of a handler. In other words, this when called callback

Links

Examples

this.onDebounce('change:x', () => {
	alert(`x = ${this.x}`); // x = 100
}, 300);

this.x = 1;

for(let i = 0; i < 100; i++) {
	this.x++;
}

Seemple#onDebounce(evtnameHandlerObject, debounceDelay, triggerOnInit, context) object

Alternative syntax: "eventname-handler" pairs

Returns object - self

Arguments

Name Type Details
evtnameHandlerObject object

An object where keys are event names and values are event handlers

debounceDelay optional number

A delay

triggerOnInit optional boolean

If triggerOnInit argument equals true, all handlers will be called immediately after event initialization

context optional object

A context of the handler

Links

Examples

this.onDebounce({
	'custom': evt => { ... },
	'click::x': evt => { ... },
	'change:y': evt => { ... }
});

Seemple#once(names, callback, context) object

Adds an event handler which can be called only once

The method works the same as Seemple#on but the passing handler can be called only once.

Note that the method has static alternative

Returns object - self

Fires addevent addevent:NAME

Arguments

Name Type Details
names eventNames

An event name or some names which are separated by a space (for example, "change:x ajaxcomplete change:y")

callback eventHandler

A handler which is caused by an event

context optional object

A context of a handler

Links

Examples

this.x = 1;

this.once('change:x', () => {
	alert('x is changed');
});

this.x = 2; // displays 'x is changed'

this.x = 3; // does nothing

Seemple#once(evtnameHandlerObject, context) object

Alternative syntax: "eventname-handler" pairs

Returns object - self

Arguments

Name Type Details
evtnameHandlerObject object

An object where keys are event names and values are event handlers

context optional object

A context of a handler

Links

Examples

this.once({
	'custom': evt => { ... },
	'click::x': evt => { ... },
	'change:y': evt => { ... }
});

Seemple#parseBindings(node, eventOptions) $nodes

Parses DOM tree, declaring property bindings on which are double braced.

Starting with 1.1 version, Seemple includes a simple DOM parser that handles double braced syntactic constructions. parseBindings method receives HTML string, DOM node or selector corresponding to DOM node as the first argument.

As the method is DOM template engine (and not HTML-replace parser), all child DOM nodes of the passed element remain in their same state (for example, DOM events aren’t erased).

The supported syntax

  1. HTML binding ```html <!-- will create text node in place of {{user.name}} and will bind name property from user object to this node JS: this.user = {name: 'Joe'}

--> Hello, {{user.name}}


2. Binding form elements.
```html
<!--
will bind "x" instance property to a text field (two-way data binding)
JS: this.x = 'some value';
-->
<input type="text" value="{{x}}">
<!--
For binding textarea and select you should use value attribute
-->
<textarea value="{{x}}"></textarea>
<select value="{{x}}">...</select>
<!--
will bind "x" instance property to a checkbox (two-way data binding)
JS: this.x = true;
-->
<input type="checkbox" checked="{{x}}">
  1. Attributes binding. ```html <!-- href attribute value will depend on "category" and "someObject.page" value (one-way data binding) JS: this.category = 'seemple'; this.someObject = { page: 42 };

--> A link

```

If you want to use something else instead of braces look at Seemple.parserBrackets

Why do we need this method?

In case if you develop a large form with standard HTML5 fields, the method will help you save time on declaring numerous bindings. Besides, parseBindings is useful in case of creating a very simple collection which doesn’t require implementation of a complicated model.

Returns $nodes - A collection of DOM nodes, which is passed to the method as an argument.

Arguments

Name Type Details
node string node $nodes

HTML string, selector, DOM node or collection of DOM nodes

eventOptions optional eventOptions

Event options which will be passed to all internal calls of Seemple#bindNode

Examples

Parsing of given node

this.parseBindings(node);

Parsing of nodes matching given selector

this.parseBindings('.my-node');

HTML string parsing

const $node = this.parseBindings('<h3>Hello, {{name}}</h3>');
this.name = 'Arthur Philip Dent';

Seemple#remove(key, eventOptions) object

Deletes a property

Returns object - self

Fires delete delete:KEY

Arguments

Name Type Details
key string

A property name or an array of names to remove

eventOptions optional eventOptions

An event options

Examples

this.remove('myKey');
this.remove(['myKey1', 'myKey2']);

Using eventOptions

this.remove('myKey', {
	silent: true
});

Seemple#select(selector) node null

Returns HTML node corresponding to a selector from a sandbox

The method is very similar to Seemple#selectAll, but it returns only one element or null.

The method has static alternative

Returns node null

Arguments

Name Type Details
selector string

A selector

Examples

this.bindNode('sandbox', '.app');
this.select('.my-element');
// the same as
this.nodes.sandbox.querySelector('.my-element');
// the same as
$('.app').find('.my-element')[0];

Seemple#selectAll(selector) $nodes

Synonym: Seemple#$

Returns HTML nodes corresponding to a selector from a sandbox

After sandbox declaration using Seemple#bindNode method, you can get its descendants. Besides, the method supports the :bound(KEY) selector.

The method has static alternative

Returns $nodes

Arguments

Name Type Details
selector string

A selector

Examples

this.bindNode('sandbox', '.app');
nodes = this.selectAll('.my-element');
// the same as
nodes = this.$('.my-element'); // $ is a short reference to selectAll
// the same as
nodes = this.$nodes.sandbox.find('.my-element');
// the same as
nodes = $('.app').find('.my-element');

The :bound(KEY) selector

this.bindNode('myKey', '.my-element');
nodes = this.selectAll(':bound(myKey) .my-another-element');
// the same as
nodes = this.$nodes.myKey.find('.my-another-element');
// the same as
nodes = $('.my-element').find('.my-another-element');

Seemple#set(key, value, eventOptions)

Sets a property value allowing to pass event options object as the third argument

The list of the supported flags:

  • silent - do not call the change and change:KEY events
  • silentHTML - do not change states of bound HTML nodes
  • force - call the change and change:KEY events even though the property value has not been changed
  • forceHTML - change a state of bound element even though the property value has not been changed. This option is usable if the bound element has been rendered after the binding (for example, some option tags have been added to select tag)
  • skipMediator - prevents the property transformation by a mediator (see Seemple#mediate)
  • skipCalc - prevents the work of dependencies created with Seemple#calc

The method has static alternative.

Fires change change:KEY beforechange beforechange:KEY

Arguments

Name Type Details
key string

A key

value *

A value

eventOptions optional eventOptions

Event options

Examples

this.on('change:myKey', evt => {
	alert(evt.value);
});

// the same as this['myKey'] = 3
// or this.myKey = 3
// alerts 3
this.set('myKey', 3);

Using eventOptions

this.on('change:myKey', evt => {
	alert(evt.value);
});

// handler isn't called
this.set('myKey', 4, {
	silent: true
});

Passing custom data to a handler

this.on('change:myKey', evt => {
	alert(evt.myCustomFlag);
});

// alerts 42
this.set('myKey', 4, {
	myCustomFlag: 42
});

Seemple#set(keyValuePairs, eventOptions)

Alternative "key-value" syntax of the Seemple#set method

Arguments

Name Type Details
keyValuePairs object

An object containing key-value pairs

eventOptions optional eventOptions

An event object

Examples

this.set({
	myKey1: 1,
	myKey2: 2
});

Passing eventOptions as second argument

this.set({
	myKey: 3
}, {
	myFlag: 'foo'
});

Seemple#trigger(names, arg) seemple

Fires an event

After adding event handlers using Seemple#on, Seemple#onDebounce or Seemple#once, any event can be fired manually using this method.

Note that the method has static alternative

Returns seemple - self

Arguments

Name Type Details
names optional eventNames

An event name or some names which are separated by a space

arg optional *

Any arguments which will be passed to every handler

Links

Examples

this.on('foo bar', (a, b, c) => {
	alert(a + b + c);
});
this.trigger('foo', 1, 2, 3); // alerts 6

Seemple#unbindNode(key, node, eventOptions) object

Destroys a binding between given property and HTML node

Using this method you can delete the binding between a property and HTML node, which has been added recently and no longer needed.

Returns object - self

Fires unbind unbind:KEY

Arguments

Name Type Details
key string null

A key or an array of keys. If you pass null instead of the key, all bindings for the given instance will be deleted

node optional string node $nodes

HTML node

eventOptions optional eventOptions

Event object ("silent" key disables firing the events "unbind" and "unbind:KEY")

Examples

this.bindNode('myKey', '.my-element');

// changes the property value and the state of the HTML element
this.myKey = true;

this.unbindNode('myKey', '.my-element');

// only the property value is being changed now
this.myKey = false;

Seemple#unbindNode(bindings, eventOptions) object

Alternative syntax which allows to pass an object with bindings to unbindNode. Look at Seemple#bindNode(2) for more information

Returns object - self

Arguments

Name Type Details
bindings object

(see the example)

eventOptions optional eventOptions

(see above)

Examples

this.unbindNode({
	foo: '.aaa'
	bar: {
		node: '.bbb'
	},
	baz: [{
		node: '.ccc'
	}, {
		node: '.ddd'
	}]
});

Seemple#unbindNode(batch, eventOptions)

Alternative syntax of the method which allows to easily unbind unlimited amount of bindings by single unbindNode call.

The variation makes possible to pass an array which includes objects with the following properties:

  • key - a property name
  • node - a node bound to key (optional)

This variation is useful because it matches one variation of Seemple#bindNode method, allowing to store bindings in a variable to easily remove them when needed.

Arguments

Name Type Details
batch array

An array of bindings

eventOptions optional eventOptions

(see above)

Examples

const temporaryBindings = [{
	key: 'a',
	node: '.my-node',
	binder: {
		setValue(v) {
			doSomething(v);
		}
	}
}, {
	key: 'b',
	node: document.querySelectorAll('.bar')
	event: {
		foo: 'bar'
	}
}, {
	key: 'c.d.e',
	node: jQuery('.baz'),
	binder: Seemple.binders.html(),
	event: {
		silent: true,
		exactKey: true
	}
}]

this.bindNode(temporaryBindings);

// these bindings are no longer needed
this.unbindNode(temporaryBindings);

Seemple#$nodes: $nodes

An object contains collections (jQuery, Zepto, built-in micro-library instance inherited from Array.prototype) of bound nodes for quick access.

Links

Examples

this.bindNode('myKey', '.my-node');
this.$nodes.myKey; // the same as $('.my-node')

Seemple#nodes: node

The object contains bound elements in the form of separate DOM nodes for quick access

Pay attention, every object property has got the first node of the bound ones to the corresponding property. Use Seemple#$nodes for getting all the nodes bound to a certain property.

Links

Examples

this.bindNode('myKey', '.my-node');
this.nodes.myKey; // the same as $('.my-node')[0]

Seemple.Class(prototype, statics) class

CommonJS module: 'seemple/class'

An implementation of classes based on the prototype inheritance

The Class function allows to use the classic OOP when ECMAScript 2015 classes cannot be used somehow.

Returns class - class (a class constructor to be exact)

Arguments

Name Type Details
prototype object

Methods and properties

statics optional object

Static methods and properties

Examples

const A = Seemple.Class({
	method1() { ... }
});

const B = Seemple.Class({
	// B is inherited from A
	extends: A,
	method2() { ... }
});

const C = Seemple.Class({
	// С is inherited from B
	extends: B,
	method2() {
		// calling the parent method
		B.prototype.method2.apply(this, arguments);
	},
	method3(a, b) { ... }
});

const D = Seemple.Class({
	// D is inherited from C
	extends: C,
	method3() {
		// calling the parent method
		C.prototype.method2.call(this, arguments);
	}
});

Passing an object with static methods and properties

const MyClass = Seemple.Class({
	method() { ... }
}, {
	staticProp: 'foo',
	staticMethod() {
		return 'bar';
	}
});

alert(MyClass.staticProp); // foo
alert(MyClass.staticMethod()); // bar

Seemple.bindNode() object

CommonJS module: 'seemple/bindnode'

Binds a property of an object to HTML node, implementing two-way data binding

This static method works the same as Seemple#bindNode and all its variations, but accepts any kind of JavaScript object as first argument.

Returns object - The first argument

Links

Examples

const object = {};
Seemple.bindNode(object, 'x', '.my-node');

Seemple.bindOptionalNode() object

CommonJS module: 'seemple/bindoptionalnode'

Initializes two-way data binding but does not throws an exception if node argument is an empty array, undefined or non-existent

This static method works the same as Seemple#bindOptionalNode and all its variations, but accepts any kind of JavaScript object as first argument.

Returns object - The first argument

Links

Examples

const object = {};
Seemple.bindOptionalNode(object, 'x', '.my-node');

Seemple.bindSandbox() object

CommonJS module: 'seemple/bindsandbox'

Binds or re-binds sandbox node

This static method works the same as Seemple#bindSandbox and all its variations, but accepts any kind of JavaScript object as first argument.

Returns object - The first argument

Links

Examples

const object = {};
Seemple.bindSandbox(object, '.my-node');

Seemple.calc() object

CommonJS module: 'seemple/calc'

Creates a dependency of one property value on values of others

This static method works the same as Seemple#calc and all its variations, but accepts any kind of JavaScript object as first argument.

Returns object - The first argument

Links

Examples

const object = {};
object.a = 40;
object.b = 2;
Seemple.calc(object, 'sum', ['a', 'b'], (a, b) => a + b);
alert(object.sum); // 42

Seemple.chain(object) object

CommonJS module: 'seemple/chain'

Allows chained calls of universal methods

The function accepts any object and returns an instance of externally inaccessible class which adopts universal methods allowing them to be called in a chain to change given object.

Universal method is a method which exist at Seemple prototype and have a static alternative (e. g. Seemple#bindNode and Seemple.bindNode)

Returns object - An instance of the class which adopts universal methods

Arguments

Name Type Details
object object function

An object

Examples

const object = {};
Seemple.chain(object)
    .calc('a', 'b', b => b * 2)
    .set('b', 3)
    .bindNode('c', '.node');

// the same as
// Seemple.calc(object, 'a', 'b', b => b * 2)
// Seemple.set(object, 'b', 3)
// Seemple.bindNode(object, 'c', '.node');

Seemple.instantiate() object

CommonJS module: 'seemple/instantiate'

Creates fixed class instance

This static method works the same as Seemple#instantiate and all its variations, but accepts any kind of JavaScript object as first argument.

Returns object - The first argument

Links

Examples

const object = {};
Seemple.instantiate(object, 'x', SomeClass);
object.x = { a: 42 };
alert(this.x instanceof SomeClass); // true
alert(this.x.a); // 42

Seemple.lookForBinder(node) binder

CommonJS module: 'seemple/lookforbinder'

Returns a binder corresponding to an element. If it is not found, it returns undefined. The function uses Seemple.defaultBinders for the search.

Returns binder - binder

Arguments

Name Type
node node

Links

Examples

const element = document.createElement('input');
element.type = 'text';

console.log(Seemple.lookForBinder(element));

// will return something similar to the following object
{
	on: 'input',
	getValue() {
		return this.value;
	},
	setValue(v) {
		this.value = v;
	}
}

Seemple.mediate() object

CommonJS module: 'seemple/mediate'

Transforms property value on its changing

This static method works the same as Seemple#mediate and all its variations, but accepts any kind of JavaScript object as first argument.

Returns object - The first argument

Links

Examples

const object = {};
Seemple.mediate(object, 'x', String);
object.x = 42;
alert(typeof object.x); // string

Seemple.off() object

CommonJS module: 'seemple/off'

Deletes an event handler

This static method works the same as Seemple#off and all its variations, but accepts any kind of JavaScript object as first argument.

Returns object - The first argument

Links

Examples

const object = {};
Seemple.on(object, 'foo', evt => {
	//...
});

Seemple.off(object, 'foo');

Seemple.on() object

CommonJS module: 'seemple/on'

Adds an event handler

This static method works the same as Seemple#on and all its variations, but accepts any kind of JavaScript object as first argument.

Returns object - The first argument

Links

Examples

const object = {};
Seemple.on(object, 'foo', evt => {
	alert(evt.hello);
});

Seemple.trigger(object, 'foo', { hello: 'World' });

Seemple.onDebounce() object

CommonJS module: 'seemple/ondebounce'

Adds an event handler which is called only once during a definite period of time

This static method works the same as Seemple#onDebounce and all its variations, but accepts any kind of JavaScript object as first argument.

Returns object - The first argument

Links

Examples

const object = {};
Seemple.onDebounce(object, 'foo', evt => {
	//...
});

Seemple.once() object

CommonJS module: 'seemple/once'

Adds event handler which can be called only once

This static method works the same as Seemple#once and all its variations, but accepts any kind of JavaScript object as first argument.

Returns object - The first argument

Links

Examples

const object = {};
Seemple.once(object, 'foo', evt => {
	//...
});

Seemple.parseBindings() $nodes

CommonJS module: 'seemple/parsebindings'

Parses DOM tree, declaring bindings with properties enclosed in double braces

This static method works the same as Seemple#parseBindings and all its variations, but accepts any kind of JavaScript object as first argument.

Returns $nodes - Resulting collection of DOM nodes

Links

Examples

const object = {};
const $node = Seemple.parseBindings(object, `
    <h3>Hello, {{name}}</h3>
`);
object.name = 'World';

Seemple.remove() object

CommonJS module: 'seemple/remove'

Deletes a property

This static method works the same as Seemple#remove and all its variations, but accepts any kind of JavaScript object as first argument.

Returns object - The first argument

Links

Examples

Seemple.remove(object, 'x');

Seemple.select() node null

CommonJS module: 'seemple/select'

Returns HTML node corresponding to a selector from a sandbox

This static method works the same as Seemple#select and all its variations, but accepts any kind of JavaScript object as first argument.

Returns node null

Links

Examples

const object = {};
Seemple.bindNode(object, 'sandbox', '.app');
Seemple.select(object, '.my-element');

Seemple.selectAll() $nodes

CommonJS module: 'seemple/selectall'

Returns HTML nodes corresponding to a selector from a sandbox

This static method works the same as Seemple#selectAll and all its variations, but accepts any kind of JavaScript object as first argument.

Returns $nodes

Links

Examples

const object = {};
Seemple.bindNode(object, 'sandbox', '.app');
Seemple.selectAll(object, '.my-element');

Seemple.set() object

CommonJS module: 'seemple/set'

Sets a property value allowing to pass an event object as the third argument

This static method works the same as Seemple#set and all its variations, but accepts any kind of JavaScript object as first argument.

Returns object - The first argument

Links

Examples

const object = {};
Seemple.set(object, 'x', 42, {
	someOption: true
});

Seemple.toSeemple() seemple

CommonJS module: 'seemple/toseemple'

The function, converting any nested tree of objects and arrays into Seemple.Object and Seemple.Array instances

Returns seemple - a newly created instance of Seemple

Examples

const seemple = Seemple.toSeemple({
	a: 1,
	b: {
		c: 2
	},
	d: [{e: 1}, {e: 2}, {e: 3}]
});

Seemple.trigger() object

CommonJS module: 'seemple/trigger'

Fires an event

This static method works the same as Seemple#trigger and all its variations, but accepts any kind of JavaScript object as first argument.

Returns object - The first argument

Links

Examples

const object = {};
Seemple.on(object, 'foo', evt => {
	alert(evt.hello);
});

Seemple.trigger(object, 'foo', { hello: 'World' });

Seemple.unbindNode() object

CommonJS module: 'seemple/unbindnode'

Breaks a binding between given property and HTML node

This static method works the same as Seemple#unbindNode and all its variations, but accepts any kind of JavaScript object as first argument.

Returns object - The first argument

Links

Examples

const object = {};
Seemple.unbindNode(object, 'x', '.my-node');

Seemple.useDOMLibrary($)

CommonJS module: 'seemple/usedomlibrary'

Forces to use a definite DOM library

By default, Seemple uses a library at window.$ reference. If there is no such a variable in the global namespace or it does not include a necessary set of methods, the built-in micro-library is used.

The useDOMLibrary method makes Seemple.js use a library you would like to use in spite of its absence in the global namespace or for a different reason (for example, if two different versions of jQuery are used on a page).

Arguments

Name Type Details
$ function

Any jQuery-like library (jQuery itself, Zepto, or null to use built-in micro library)

Examples

Seemple.useDOMLibrary(jQuery.noConflict());

Seemple.defaultBinders: array

CommonJS module: 'seemple/defaultbinders'

An array of functions which return a corresponding binder

defaultBinders is the array of functions which check an element in turn against given rules in these functions and return a binder (see binder). This array is used when the third argument has not been passed to the Seemple#bindNode method. See more detailed information about bindings in Seemple#bindNode documentation.

Links

Examples

Seemple.defaultBinders.unshift(element => {
	// check if the element has "foo" class name
	if(element.classList.contains('foo')) {
		// if checking is OK, return a new binder
		return {
			on: ...,
			getValue: ...,
			setValue: ...
		};
	}
});

// ...

this.bindNode('myKey', '.foo.bar');

Seemple.parserBrackets: object

CommonJS module: 'seemple/parserbrackets'

Contains brackets for bindings parser

parserBrackets object allows to change default syntax of the parser. It contains two properties: left (left bracket, "{{" by default) and right (right bracket, "}}" by default)

Examples

Use syntax [[=property]] instead of {{property}}

Seemple.parserBrackets.left = '[[=';
Seemple.parserBrackets.right = ']]';

Seemple.binders object

CommonJS module: 'seemple/binders'

A namespace for binders. Out of the box it contains general-purpose binders and can be extended by you so as not to make a mess of the global namespace.

Take a little agreement into consideration: every property from the Seemple.binders namespace must be made as a function which returns a binder (such functions usually called "binder creators").

At this documentation the properties from Seemple.binders are used directly. But to make your code more readable it's recommended to assign them to a variable.

const html = Seemple.binders.html;

// ...
this.bindNode('x', node, html());

Or import them as CJS/ES2015 module:

// import few binders per once
import { html, text, prop } from 'seemple/binders';

// import binders separately
import html from 'seemple/binders/html';

Links

Examples

Seemple.binders.myCoolBinder = (var1, var2) => {
	return {
		on: 'click',
		getValue() { ... },
		setValue() { ... },
		initialize() { ... },
		destroy() { ... }
	};
};

this.bindNode('myKey', '.my-element',
	Seemple.binders.myCoolBinder('Hello', 'World'));

Seemple.binders.attr(attribute, mappingFn) binder

Returns a binder which changes an attribute of DOM node depending on an object property value

The value can be transformed using mappingFn argument.

Returns binder

Arguments

Name Type Details
attribute string

Attribute name

mappingFn optional function

Mapping function

Examples

this.bindNode('image', 'img.my-image', Seemple.binders.attr('src'));

this.image = 'http://example.com/cats.jpg';

The usage of mapping function

this.bindNode('myKey', '.my-node',
    Seemple.binders.attr('foo', value => `Hello, ${value}`));

this.myKey = 'World'; // the foo attr now has value "Hello, World"

Seemple.binders.className(className, bool=true) binder

Returns a binder which switches over DOM node class name depending on an object property value. If property value equals true non-strictly, a class name is added, otherwise - it's removed. The logic can be changed by passing false as the second argument and in this way, a class name will be added when a property value equals false non-strictly and vice versa.

Returns binder

Arguments

Name Default Type
className string
bool optional true boolean

Examples

this.bindNode('myKey', '.my-element',
        Seemple.binders.className('foo'));

this.myKey = true; // adds the 'foo' class

this.myKey = false; // removes the 'foo' class
this.bindNode('myKey', '.my-element',
        Seemple.binders.className('foo', false));

this.myKey = false; // adds the 'foo' class

this.myKey = true; // removes the 'foo' class

Seemple.binders.dataset(property, mappingFn) binder

Returns a binder which changes given dataset property of bound DOM node depending on an object property value.

The property value can be transformed using mappingFn argument.

Returns binder

Arguments

Name Type Details
property string

A property of dataset object

mappingFn optional function

Mapping function

Examples

this.bindNode('myKey', '.my-element', Seemple.binders.dataset('myProp'));
this.myKey = 'foo';

The usage of mapping function

this.bindNode('myKey', '.my-element',
    Seemple.binders.dataset('myProp', value => `Hello, ${value}`));

this.myKey = 'foo'; // the attr data-my-prop now has value "Hello, foo"

Seemple.binders.display(bool=true) binder

Returns a binder which controls a visibility of DOM node (using style.display) depending on an object property value

Returns binder

Arguments

Name Default Type Details
bool optional true boolean

If the argument equals true, a node is hidden when a property value is falsy; if it equals false, it is hidden when a property value is truly.

Examples

this.bindNode('myKey', '.my-element', Seemple.binders.display(true));
this.bindNode('myKey', '.my-element', Seemple.binders.display(false));

Seemple.binders.existence(bool=true) binder

Returns a binder which controls an existence of DOM node at DOM tree depending on an object property value

The binder works the same way as Seemple.binders.display, but instead of visibility change the existence at page DOM is changed. The binder is useful for:

  • Big appications: show one or another page depending on route state;
  • For infinite scrolling;
  • For other cases where you need to hide an element but its existence at DOM tree isn't necessary.

Returns binder

Arguments

Name Default Type Details
bool optional true boolean

If the argument equals true, a node is removed when a property value is falsy; if it equals false, it is removed when a property value is truly

Examples

this.bindNode('myKey', '.my-element', Seemple.binders.existence(true));
this.bindNode('myKey', '.my-element', Seemple.binders.existence(false));

Seemple.binders.html(mappingFn) binder

Returns a binder which changes innerHTML of bound DOM node depending on an object property value

The property value can be transformed using mappingFn argument.

Returns binder

Arguments

Name Type Details
mappingFn optional function

Mapping function

Examples

this.bindNode('myKey', '.my-element', Seemple.binders.html());
// sets innerHTML="<div>foo</div>"
this.myKey = '<div>foo</div>';

The usage of mapping function

this.bindNode('myKey', '.my-element',
    Seemple.binders.html(value => `Hello, ${value}`));

// sets innerHTML="Hello, <div>foo</div>"
this.myKey = '<div>foo</div>';

Seemple.binders.input(type) binder

Returns a binder which binds an object property to an input element value. It is not obligatory to use the binder directly because it is included in the Seemple.defaultBinders list.

Returns binder

Arguments

Name Type Details
type optional string

Input type

Examples

this.bindNode('myKey', '.my-input', Seemple.binders.input('range'));

Seemple.binders.output() binder

Returns a binder which binds an object property to an output element value. It is not obligatory to use the binder directly because it is included in the Seemple.defaultBinders list.

Returns binder

Examples

this.bindNode('myKey', '.my-output', Seemple.binders.output());

Seemple.binders.progress() binder

Returns a binder which binds an object property to a progress element value. It is not obligatory to use the binder directly because it is included in the Seemple.defaultBinders list.

Returns binder

Examples

this.bindNode('myKey', '.my-progres', Seemple.binders.progress());

Seemple.binders.prop(property, mappingFn) binder

Returns a binder which changes given property of DOM node depending on an object property value

The property value can be transformed using mappingFn argument.

Returns binder

Arguments

Name Type Details
property string

A property name

mappingFn optional function

Mapping function

Examples

this.bindNode('disabled', '.my-button',
        Seemple.binders.prop('disabled'));

// sets disabled = true property for the node
this.disabled = true;

// sets disabled = false property for the node
this.disabled = false;

The usage of mapping function

this.bindNode('myProp', '.my-node'
    Seemple.binders.prop('foo', value => `Hello, ${value}`));

// foo property of the element now has value "Hello, World"
this.myProp = 'World';

Seemple.binders.select(multiple) binder

Returns a binder which binds an object property to a select element value. It is not obligatory to use the binder directly because it is included in the Seemple.defaultBinders list.

Returns binder

Arguments

Name Type Details
multiple optional boolean

If select is multiple

Examples

this.bindNode('myKey', '.my-select', Seemple.binders.select(true));

Seemple.binders.style(property, mappingFn) binder

Returns a binder which changes given style property of bound DOM node depending on an object property value

The property value can be transformed using mappingFn argument.

Returns binder

Arguments

Name Type Details
property string

A property of style object (camel-cased)

mappingFn optional function

Mapping function

Examples

this.bindNode('myKey', '.my-progres',
        Seemple.binders.style('backgroundColor'));
this.myKey = 'red'; // background-color of .my-progress is red now

The usage of mapping function

this.bindNode('myKey', '.my-element',
  Seemple.binders.style('backgroundImage', value => `url("${value}")`));

this.myKey = 'cats.jpg'; // backgroundImage now equals to "url("cats.jpg")"

Seemple.binders.text(mappingFn) binder

Returns a binder which changes textContent of bound DOM node depending on an object property value

The property value can be transformed using mappingFn argument.

Returns binder

Arguments

Name Type Details
mappingFn optional function

Mapping function

Examples

this.bindNode('myKey', '.my-element', Seemple.binders.text());
this.myKey = 'foo'; // sets textContent as "foo"

The usage of mapping function

this.bindNode('myKey', '.my-element',
    Seemple.binders.text(value => `Hello, ${value}`));

this.myKey = 'foo'; // sets textContent as "Hello, foo"

Seemple.binders.textarea() binder

Returns a binder which binds an object property to a textarea element value. It is not obligatory to use the binder directly because it is included in the Seemple.defaultBinders list.

Returns binder

Examples

this.bindNode('myKey', '.my-textarea', Seemple.binders.textarea());

Class Seemple.Object

CommonJS module: 'seemple/object'

Seemple.Object is a class which is responsible for key-value data. Its goal is to separate "service" properties from data that can be passed to a server or kept in a local storage. The class is inherited from the Seemple class and includes all its properties and methods.

Imagine you create an object which includes "a", "b" and "c" properties. Let's assume that "a" and "b" are the properties which must be sent to a server, and "c" property is just responsible for some application state (for example, it contains the sum of "a" and "b"). The server does not need the "c" property. So we have to separate the properties which are responsible for data from ones which are not.

In order to declare such properties from others, you can make use of the Seemple.Object#addDataKeys method.

this.addDataKeys(['a', 'b']);

this.a = 1;
this.b = 2;
this.c = 3;

If you don't know which properties are specified in advance, you can always use the Seemple.Object#setData method, which declares not only properties responsible for data but sets their values at once.

this.setData({
    a: 1,
    b: 2
});

this.c = 3;

After an application has found out what is data, Seemple.Object instance can be converted into an ordinary object by the Seemple.Object#toJSON method and passed to a server or kept in a local DB (for example, in localStorage).

// will return an object { a: 1, b: 2 }
this.toJSON();

Events

On add or change of data properties set and modify events are fired. On remove of data properties remove and modify events are fired. That means you can listen any changes of Seemple.Object instance with the only modify event name.

this.on('modify', () => {
    alert('an object is modified');
});

Arguments

Name Type Details
data optional object Data

Examples

Creation of an instance with two specified properties

// the same as new Seemple.Object().setData({ a: 1, b: 2 });
new Seemple.Object({ a: 1, b: 2 });

The inheritance

class MyClass extends Seemple.Object {
	constructor(data) {
		super(data).sayHello();
	}
	sayHello() {
		alert("Hello World!");
	}
}

The inheritance using Seemple.Class function

const MyClass = Seemple.Class({
	extends: Seemple.Object,
	constructor(data) {
		this.setData(data).sayHello();
	},
	sayHello() {
		alert("Hello World!");
	}
});

Data enumerating, using for..of

const seempleObject = new Seemple.Object({ a: 1, b: 2 });
for(let item of seempleObject) {
	console.log(item); // 1 .. 2
}

Seemple.Object#addDataKeys(keys) seempleObject

Adds given property names to a list of property names which are responsible for data

This method is used when you need to declare keys which are responsible for data but values of corresponding properties are not known yet.

Returns seempleObject - self

Fires set modify

Arguments

Name Type Details
keys string array

An array of property names or a list of args

Examples

this.addDataKeys(['a', 'b']);
this.addDataKeys('a', 'b');

Seemple.Object#each(callback, thisArg) seempleObject

Iterates properties which are responsible for data through callback function

This method is very similar to Array.prototype.forEach. It allows to iterate over data properties when for..of syntax is not available.

Returns seempleObject - self

Arguments

Name Type Details
callback function

A function which is called on every iteration

thisArg optional *

A context of a function

Examples

this
	.setData({ a: 1, b: 2 })
	.addDataKeys('c')
	.each((value, key) => {
		console.log(key, value);
	}, this);
;
// >>> a 1, b 2, c undefined

Seemple.Object#entries() array

Returns an array of [key, value] pairs of properties which are responsible for data

Returns array - entries

Examples

for(const [key, value] of this.entries()) {
    console.log(key, value);
}

Seemple.Object#isDataKey(key) boolean

Checks if given string a name of data property

Returns boolean - a result of checking

Arguments

Name Type
key string

Examples

console.log(this.isDataKey('a'));

Seemple.Object#keyOf(value) string null

Searches for specified property value among others which are responsible for data and returns one key if this value has been found (very similar to Array.prototype.indexOf function)

Returns string null

Arguments

Name Type Details
value *

A value of any type which should be found among data keys

Examples

const seempleObject = new Seemple.Object({
	a: 1,
	b: 2
});

seempleObject.c = 3;

seempleObject.keyOf(1); // 'a'

seempleObject.keyOf(2); // 'b'

seempleObject.keyOf(3); // null

Seemple.Object#keys() array

Returns an array of property names which are responsible for data

Returns array - keys

Examples

const keys = this.keys();

Seemple.Object#removeDataKeys(keys) seempleObject

Deletes given property names from a list of property names which are responsible for data (but does not delete the properties)

Returns seempleObject - self

Fires remove modify

Arguments

Name Type Details
keys string array

An array of property names or a list of args

Examples

this.removeDataKeys(['a', 'b']);
this.removeDataKeys('a', 'b');

Seemple.Object#setData(key, value, eventOptions) seempleObject

Synonym: Seemple.Object#jset

Sets a property and adds property name to a list of property names which are responsible for data

This method does two things:

  1. Sets a specified value to a given property.

  2. Adds a key to data keys list which it makes property available for using in the Seemple.Object#each, Seemple.Object#keys, Seemple.Object#toJSON and other methods.

When replaceData flag is set as true then all other properties will be removed from the list of data properties.

Apart from that, the method works the same as Seemple#set.

Returns seempleObject - self

Fires change change:KEY modify set

Arguments

Name Type Details
key string

A key

value *

A value

eventOptions optional eventOptions

An event options

Links

Examples

this.setData('a', 1).setData('b', 2);

// assign 3 to the 'c' property,
// but do not add the key to a list of keys which are responsible for data
this.set('c', 3);

this.each((value, key) => {
	console.log(key, value);
});
// displays  'a' 1 and 'b' 2

console.log(this.keys()); // displays  ['a', 'b']

console.log(this.toJSON()); // displays  { a: 1, b: 2 }

After using the Seemple.Object#setData method, you can work with a property like with an ordinary one

this.setData('a', 1).setData('b', 2);
this.set('a', 3);
this.b = 4;

The use of alternative method name jset

this.jset('a', 1);

Seemple.Object#setData(keyValuePairs, evtOpts) seempleObject

Alternative syntax of the Seemple.Object#setData method that uses a "key-value" object for setting several properties at once

Returns seempleObject - self

Arguments

Name Type Details
keyValuePairs object

A key-value object

evtOpts eventOptions

A event object

Examples

this.setData({
	a: 1,
	b: 2
});

If replaceData event option set as true, then other properties will be removed from a list of data properties

this
	.addDataKeys(['a', 'b', 'c'])
	.setData({
		a: 1,
		b: 2
	}, {
		replaceData: true
	});

console.log(this.keys()); // ['a', 'b']

Seemple.Object#toJSON(recursive=true) object

Converts an instance and its internal properties into an ordinary object

The method works recursively, calling toJSON for all internal properties which have such method. To cancel this behavior pass false as the only argument.

Returns object

Arguments

Name Default Type
recursive optional true boolean

Examples

const seempleObject = new Seemple.Object({
	a: 1,
	b: 2,
	c: new Seemple.Object({
		d: 3,
		e: 4
	})
});

// returns {a: 1, b: 2, c: { d: 3, e: 4 }}
console.log(seempleObject.toJSON());

// returns {a: 1, b: 2, c: SeempleObject}
console.log(seempleObject.toJSON(false));

Seemple.Object#values() array

Returns an array of property values which are responsible for data

Returns array - values

Examples

const values = this.values();

Class Seemple.Array

CommonJS module: 'seemple/array'

The Seemple.Array serves as collection class in Seemple framework. It is inherited from the Seemple class, obtaining all parent possibilities without exception. Besides, Seemple.Array has all methods that ordinary array has.

All the methods which have been adopted from the built-in Array work like their originals

A programmer familiar to the methods of native Array can understand immediately by means of which method adds (push, unshift, splice), deletes (pop, shift, splice), sorts (sort, reverse) items etc.

For the reason that methods work the same as original ones (with a few exceptions), they are not presented in this documentation separately but they are gathered in the Seemple.Array#METHOD section.

this.push(1, 2, 3);
this.pop();

All methods adopted from built-in Array which modify an array can be called with an event object passing

The method_ syntax is used for this, where underscore at the end of a method name means that the last argument is an event object. Such methods are not given in this documentation because it is necessary to remember their syntax only. See Seemple.Array#METHOD_.

this.push_(1, 2, 3, {
    silent: true
});

this.pop_({
    foo: 'bar'
});

A developer has an opportunity of catching any data modification

While using a methods adopted from built-in Array, events with corresponding name are fired. Calling the push method, the push event is fired, calling the shift method, the shift event is fired, calling the sort method, the sort event is fired, and so on.

this.on('push', evt => {
    console.log('push is called');
});

this.push(1, 2, 3);

While adding items, add and addone events are fired. The first one is fired once on adding (for example, you have added few items using push and the event has been fired only once), the second one is fired once on every added item. On the add event triggering, an array of added items is passed to an event object as added property and on the addone triggering, each particular added item is passed to it as addedItem property.

this.on('add', evt => {
    console.log(evt.added); // [1,2,3]
});

this.push(1, 2, 3);
// the handler will be called three times,
// as three new item have been added to the array
this.on('addone', evt => {
    console.log(evt.addedItem); // 1 ... 2 ... 3
});

this.push(1, 2, 3);

On removing items the same logic is observed: remove triggers once even though several items have been removed (removed items are contained in the removed property) and the removeone event triggers for each removed item individually (removed item is contained in the removedItem property).

this.push(1, 2, 3, 4, 5);

this.on('remove', evt => {
    console.log(evt.removed); // [2, 3, 4]
});

this.splice(1, 3);
this.push(1, 2, 3, 4, 5);

// the handler will be called three times,
// as three items have been removed from the array
this.on('removeone', evt => {
    console.log(evt.removedItem); // 2 ... 3 ... 4
});

this.splice(1, 3);

On every modification of an array the modify event is fired, allowing to catch all changes in the array (adding, removing, re-sorting).

this.on('modify', evt => {
    console.log(evt.added);
    console.log(evt.removed);
});

length is an ordinary property which can be bound to HTML node or you can listen changes using the change:length event name.

For example, on adding three items using the push method with three arguments, the following events: push, add, addone (three times), modify, change:length are fired.

Model

The Seemple.Array#Model property specifies a class of items that an array contains. It is recommended to inherit Model from the Seemple.Object class or the Seemple.Array one (in case if it is necessary to get a collection of collections) in order to get the opportunity of converting an array into an ordinary one recursively by using the Seemple.Array#toJSON method.

Automatic rendering

Seemple.Array can render HTML nodes on a page automatically in any modifications of an array. The Seemple.Array#itemRenderer property is used for that. You do not have to worry about rebuilding the HTML tree, Seemple.Array does it for you. For detailed information read the documentation of Seemple.Array#itemRenderer.

Links

Examples

An instance creation

new Seemple.Array();

An instance creation with length specifying

new Seemple.Array(42);

Items passing on creation

new Seemple.Array('Hi', {a: 'b'});

The inheritance

class MyClass extends Seemple.Array {
	constructor(items) {
		super(...items).sayHello();
	}
	sayHello() {
		alert("Hello World!");
	}
}

The inheritance using Seemple.Class function

const MyClass = Seemple.Class({
	extends: Seemple.Array,
	constructor(items) {
		this.recreate(items).sayHello();
	},
	sayHello() {
		alert("Hello World!");
	}
});

Seemple.Array#METHOD()

Any method from Array.prototype

Seemple.Array includes all the methods existing in the native JavaScript Array:

Yet they work the same as the Array.prototype. There are only a few remarks:

  • The forEach method returns an array itself instead of undefined
  • The methods which should return a new array (splice, slice, filter, map...) return a new Seemple.Array instance.
  • The keys, values and entries methods return an array instead of an iterator.

Moreover, the methods generate events which are fired on any array modification. For more detailed information look at Seemple.Array.

Links

Examples

this.push(1, 2, 3);
const seempleArray = this
	.forEach((value, index) => {
		//...
	})
	.map((value, index) => {
		//...
	});

console.log(seempleArray.isSeempleArray); // true
this.reverse();

Seemple.Array#METHOD_()

Any method from Array.prototype with the possibility of passing event object

Having found out more about Seemple.Array#METHOD, it becomes clear that the methods do not support an event object passing as they exactly duplicate the syntax and the number of arguments of the built-in Array. The METHOD_ syntax allows to pass some data to event handlers.

The list of available flags:

Links

Examples

this.push_(1, 2, 3, {
    silent: true
});

this.pop_({
    silent: true
});
this.on('modify', evt => {
	alert(evt.flag); // 42
});

this.push_(1, 2, 3, {
	flag: 42
});

<virtual>Seemple.Array#Model(data, seempleArray, index)

The property defines a class of items which will be inserted to a collection

Every time items are added to an array, the built-in handler checks if added item is Model instance and converts it into the one if the check fails. It is recommended to inherit Model from the Seemple.Object or Seemple.Array class (in case if it is necessary to get a collection of collections) to get an opportunity of an array conversion into ordinary one by Seemple.Array#toJSON method.

Use Seemple.Array#mediateItem for more flexible control of an item class (for example, if you need to use one class for certain items and another one - for others).

Arguments

Name Type Details
data object

Data which have been passed to a constructor

seempleArray seempleArray

An array where an item has been added to

index number

Current index of an instance in the parent array

Links

Examples

// define a model
class MyModel extends Seemple.Object {
	constructor(data, parentArray, index) {
		super(data);
		this.doSomething();
	}
	doSomething() { ... }
}

// define collection class
class MyArray extends Seemple.Array {
	get Model() {
		return MyModel;
	}
}

const myArray = new MyArray();

// add two items
myArray.push({
    a: 1,
    b: 2
}, {
    a: 3,
    b: 4
});

console.log(myArray[0] instanceof MyModel); // true
console.log(myArray[1] instanceof MyModel); // true

// returns [{ a: 1, b: 2 }, { a: 3, b: 4 }]
myArray.toJSON();

Seemple.Array#mediateItem(mediator)

Transforms an item value

This method is used to transform Seemple.Array items. Note that calling of this method overrides the Seemple.Array#Model property.

Arguments

Name Type Details
mediator function

A function which should return transformed value of an item

Links

Examples

// all the array items are integers
this.mediateItem(item => parseInt(item) || 0);
this.push(1, 2, 3, 4, 5);

// all the array items are strings
this.mediateItem(String);

this.push(6, 7);

this.unshift(true, {});

// ["true", "[object Object]", "1", "2", "3", "4", "5", "6", "7"]
console.log(seempleArray.toJSON());
this.mediateItem(item => {
	if(item.something) {
		return new FirstModel(item);
	} else {
		return new SecondModel(item);
	}
});

<virtual>Seemple.Array#onItemRender(item, renderEvent)

A function which is called before "render" event

The virtual method can be used instead of "render" event handler.

At the same time onRender method is called in a rendered item with the only argument - an event object.

Arguments

Name Type Details
item object

A rendered node of a collection

renderEvent object

"render" event object

Examples

class MyModel extends Seemple.Object {
	constructor(data) {
		super(data);
	}
	onRender(renderEvt) {
		this.bindNode('isChecked', ':sandbox .my-checkbox');
		this.bindNode('text', ':sandbox .text', Seemple.binders.html());
	}
});

class MyArray extends Seemple.Array {
	get Model() {
		return MyModel;
	}
	itemRenderer() {
		return '<li>'
	}
	constructor() {
		this.bindNode('sandbox', '.my-form');
	}
	onItemRender(item, renderEvt) {
		// ...
	}
});

const app = new MyArray();

Seemple.Array#orderBy(keys, orders=asc) seempleArray

Sorts by properties of array items

This method works almost like orderBy from lodash. It accepts a key or a list of keys as the first arg and an order (asc/desc) or a list of orders as the second arg.

Returns seempleArray - self

Fires sort

Arguments

Name Default Type Details
keys string array

A key or a list of keys

orders optional asc string array

An order or a list of orders corresponding to the list of keys

Examples

A little example taken from lodash documentation

this.recreate([
    { 'user': 'fred',   'age': 48 },
    { 'user': 'barney', 'age': 34 },
    { 'user': 'fred',   'age': 42 },
    { 'user': 'barney', 'age': 36 }
]);

// sort by 'user' in ascending order and by 'age' in descending order
this.orderBy(['user', 'age'], ['asc', 'desc']);
// → [{"user":"barney","age":36},{"user":"barney","age":34},{"user":"fred","age":48},{"user":"fred","age":42}]

Seemple.Array#pull(indexOrValue, eventOptions) * null

Removes an item with specified index from an array or an item itself

Returns * null - A deletee or null

Fires pull remove removeone modify

Arguments

Name Type Details
indexOrValue object number

An item index (a number) which has to be removed or the deletee itself (an object)

eventOptions optional eventOptions

An event object in case if it is needed to pass some data to event handlers or to set some service flags (eg. silent)

Examples

Passing of an index

let removed;

this.recreate(['a', 'b', 'c']);

removed = this.pull(1);

console.log(removed); // 'b'

console.log(this.join(',')); // 'a,c'

Passing of a deletee object

const object1 = {};
const object2 = {};
const object3 = {};
let removed;

this.push(object1, object2, object3);

removed = this.pull(object2);

console.log(removed === object2); // true

console.log(this.length); // 2

Seemple.Array#recreate(array, eventOptions) seempleArray

Recreates the Seemple.Array instance

The method allows to convert any array or array-like into the Seemple.Array instance. If nothing has been passed as the first argument, the instance is cleansed.

Returns seempleArray - self

Fires recreate modify add addone remove removeone

Arguments

Name Type Details
array optional array

An array or array-like object

eventOptions optional eventOptions

An event object

Links

Examples

// cleanse the array and add 5 new items
this.recreate([1, 2, 3, 4, 5]);

// cleanse the array
this.recreate();

Seemple.Array#rerender(eventOptions) seempleArray

Rerenders DOM nodes of items which are included into an instance

This method renders array items in an array container. If a node which is associated to an array item has already been created, the method reinserts it into container of the array instead of rerendering it all over again.

The method can be useful in case when items have been added to the array before declaring a sandbox or a container.

To force items rerender (e. g. you use custom template engine) pass a property forceRerender with true value to an event object.

Returns seempleArray - self

Arguments

Name Type Details
eventOptions optional eventOptions

An event options

Examples

this.rerender({
	forceRerender: true
});

Seemple.Array#restore(selector, eventOptions) seempleArray

Restores Seemple.Array instance from existing HTML nodes on a page.

In case if a collection is pre-rendered on the page (e. g. via webserver), the method can restore the collection from existing HTML nodes.

<!-- One, Two, Three are prerendered -->
<ul class="collection-node">
    <li>One</li>
    <li>Two</li>
    <li>Three</li>
    <script type="text/html" class="renderer">
        <li></li>
    </script>
</ul>
class MyModel extends Seemple.Object {
    constructor(data) {
        super(data);
        this.addDataKeys('value');
    }
    onRender() {
        this.bindNode('value', ':sandbox', Seemple.binders.html())
    }
});

class MyCollection extends Seemple.Array {
    get itemRenderer() {
        return ':sandbox .renderer';
    }
    constructor() {
        this
            .bindNode('sandbox', '.collection-node')
            .restore(':sandbox li');
    }
});

const myCollection = new MyCollection();
myCollection.push({
    value: 'Four'
});

console.log(myCollection.toJSON());
// [{value: 'One'}, {value: 'Two'}, {value: 'Three'}, {value: 'Four'}]

If selector arg isn't passed then the collection will be restored from child nodes that placed in a container ("container" or "sandbox").

The method fires "render" event and calls onRender and onItemRender functions (look at the docs) for every newly added item similar to usual rendering.

Returns seempleArray - self

Fires recreate modify add addone

Arguments

Name Type Details
selector optional selector

A selector

eventOptions optional eventOptions

An event options

Seemple.Array#toJSON(recursive=true) array

Converts Seemple.Array instance into native array

The method works recursively, calling toJSON for all items which have such method. To cancel this behavior pass false as the only argument.

Returns array

Arguments

Name Default Type
recursive optional true boolean

Examples

const seempleArray = new Seemple.Array([1, 2, new SeempleArray(3, 4)]);

// returns [1, 2, [3, 4]]
console.log(seempleArray.toJSON());

// returns [1, 2, SeempleArray]
console.log(seempleArray.toJSON(false));

<virtual>Seemple.Array#itemRenderer: string function

HTML string, a selector or a function which is responsible for rendering DOM nodes of an array on a page

The itemRenderer overridable property allows to render corresponded HTML nodes of items of an array without a programmer's participation. On a new item insertion into an array, HTML node is created automatically. This node becomes a sandbox (see. Seemple#bindNode) (this behavior can be canceled, read below) for inserted item and it is inserted into HTML container which is defined in an array.

For brevity, the "class fields" syntax will be used in examples at this article.

Where is created node inserted?

In order to define a place where rendered HTML nodes will be inserted, it is necessary to define a container. HTML sandbox should be declared for an array or a special container key should be bound to the HTML container for that. Read more detailed information about bindings and sandboxes in Seemple#bindNode.

Rendering to a sandbox:

<ul class="my-list"></ul>
class MyArray extends Seemple.Array {
    itemRenderer = '<li>';
    get Model() { return MyModel; }
    constructor() {
        // sandbox definition
        super().bindNode('sandbox', '.my-list');
    }
});

Now all newly created <li> nodes will get into the .my-list node.

If you do not want to insert HTML nodes straight into the sandbox, you can bind container property to needed HTML node. This logic is required in case if a sandbox is not limited by collection items and it includes other HTML nodes.

<div class="my-widget">
    <h1>This is my awesome list</h1>
    <ul class="my-list"></ul>
</div>
class MyArray extends Seemple.Array {
    itemRenderer = '<li>';
    get Model() { return MyModel; }
    constructor() {
        super();
        // sandbox definition
        this.bindNode('sandbox', '.my-widget');
        // a definition of container for HTML nodes
        this.bindNode('container', '.my-list');
    }
}

In the example above the HTML nodes will be inserted into .my-list instead of .my-widget.

The itemRenderer property supports a few variants of defining, but they all must contain or return the only HTML node.

HTML string as a property value

class MyArray extends Seemple.Array {
    get Model() { return MyModel; }
    itemRenderer = '<div class="my-div">foo</div>';
    constructor() { ... }
}

A selector as a property value

In case if you carry over the templates for the items to the HTML page, itemRenderer supports the selector as a value. When this occurs, Seemple.Array will search for HTML node in DOM tree and it will extract innerHTML of found node. In case if a node is not found, an exception will be thrown.

HTML string is different from a selector due to the presence of the < symbol in a string.

<script type="text/html" id="my-template">
    <div class="my-div">foo</div>
</script>
class MyArray extends Seemple.Array {
    get Model() { return MyModel; }
    itemRenderer = '#my-template';
    constructor() { ... }
}

A function as a property value

The usage of a function as the itemRenderer property value gives an additional code flexibility if it is necessary to dynamically generate HTML node for rendering. A function can return:

HTML string

class MyArray extends Seemple.Array {
    itemRenderer() {
        return '<div class="my-div">foo</div>';
    }
}

A selector

class MyArray extends Seemple.Array {
    itemRenderer() {
        return '#my-template';
    }
}

DOM node

class MyArray extends Seemple.Array {
    itemRenderer() {
        return document.createElement('div');
    }
}

Parent renderer overlapping by the render property

Sometimes it is convenient to declare a renderer inside a model class. The renderer property overlaps the itemRenderer value if it is specified for a child item of a collection.

class MyModel extends Seemple.Object {
    renderer = '<div class="my-div">foo</div>';
}

class MyArray extends Seemple.Array {
    Model = MyModel,
    itemRenderer = '<frameset>bar</frameset>';
    constructor() { ... }
}

In this case you do not have to designate itemRenderer at all because render of a child item adopts all its roles. The syntax remains the same: HTML string, a selector or a function can be used.

render and afterrender events

After an item has been inserted into an array and its HTML node has already been created but it hasn't been inserted into array container yet, the render event is fired on inserted item. After its triggering you can declare needed data bindings.

afterrender is fired when HTML node is inserted into the container.

<form class="my-form"></form>
class MyModel extends Seemple.Object {
    constructor(data) {
        super(data);

        // wait for the event triggering
        this.on('render', () => {
            // define bindings
            this.bindNode('isChecked', ':sandbox .my-checkbox');
            this.bindNode('text', ':sandbox .text',
                Seemple.binders.html());
        });
    }
});

class MyArray extends Seemple.Array {
    get Model() { return MyModel; }
    itemRenderer = `<label>
        <input type="checkbox" class="my-checkbox">
        <span class="text"></span>
    </label>`;
    constructor() {
        super();
        this.bindNode('sandbox', '.my-form');
        this.push({
            isChecked: true,
            text: 'Buy a raccoon'
        }, {
            isChecked: false,
            text: 'Sell the raccoon'
        });
    }
});

const app = new MyArray();

The code above will create the following HTML tree:

<form class="my-form">
    <label>
        <input type="checkbox" class="my-checkbox">
        <span class="text">Buy a raccoon</span>
    </label>
    <label>
        <input type="checkbox" class="my-checkbox">
        <span class="text">Sell the raccoon</span>
    </label>
</form>

And it will bind the checkboxes to the corresponding isChecked and text properties.

Remember, the opportunity of listening delegated events is implemented in the framework. The array can listen to an event of an item rendering, using the *@render event name (see the documentation of eventNames).

this.on('*@render', () => {
    alert('Child item is rendered');
});

Rendered HTML node becomes a sandbox for inserted item allowing to use the :sandbox selector and some other possibilities after rendering. If an item enters a few collections at once, set the bindRenderedAsSandbox property to false for an item to cancel this behavior.

class MyModel extends Seemple.Object {
    bindRenderedAsSandbox = false;
    // ...
});

onItemRender and onRender

To improve the readability of code there is a virtual method called Seemple.Array#onItemRender, which can be used instead of render event. As second alternative, the onRender can be used for a "model".

class MyModel extends Seemple.Object {
    constructor(data) {
        super(data);
    },
    onRender(evt) {
        this.bindNode('isChecked', ':sandbox .my-checkbox');
        this.bindNode('text', ':sandbox .text',
                Seemple.binders.html());
    }
}

class MyArray extends Seemple.Array {
    get Model() { return MyModel; }
    itemRenderer = '...';
    constructor() {
        //...
    },
    onItemRender(item, evt) {
        //...
    }
}

const app = new MyArray();

A template engine

If you have a look at the examples of using Seemple.Array and Seemple.Array#itemRenderer, you can notice that all logic which is responsible for the two-way and one-way data binding is contained in JavaScript code. But when you develop a very simple collection which does not include complicated logic, lots of bindings, etc, you would like to have a shorter variant of the binding declaration. A template including bindings (see Seemple#parseBindings) can be passed to itemRenderer for that.

class MyArray extends Seemple.Array {
    itemRenderer: `<label>
        <input type="checkbox" checked="{{isChecked}}">{{text}}
    </label>`
    // ...
}

const app = new MyArray();

The cancellation of rendering

As is seen from above if the render property of the child item is specified, Seemple.Array will try to render it. In order to completely cancel rendering for an array, set a property renderIfPossible of Seemple.Array instance as false.

class MyArray extends Seemple.Array {
    renderIfPossible = false;
    // ...
}

Moving an object across few collections

By default, when you add an object to an array the framework will try to render it using itemRenderer. It gives a great advantage in cases when you have two or more lists which include the same object. The lists react instantly on any change in the object.

But sometimes you just need to move an object across few collections without re-rendering it again. If you need to move object to another collection including its sandbox use the moveSandbox flag while adding the new item to another array.

this.push_(item, {
    moveSandbox: true
})

itemRenderer reassigning

When you reassign itemRenderer a collection rerenders automatically.

this.itemRenderer = '<div class="new-renderer"></div>';

This ability allows to load renderer from a server.

fetch('templates/template.html')
    .then(resp => resp.text())
    .then(data => {
        this.itemRenderer = data;
    });

To render the only objects which aren't yet rendered use Seemple#set with forceRerender flag set to false.

this.set('itemRenderer', renderer, {
    forceRerender: false
});

It's useful when you use server prerendering (look at the Seemple.Array#restore) and template needs to be loaded asynchronously.

class MyArray extends Seemple.Array {
    constructor() {
        super()
            .bindNode('sandbox', '.some-node')
            .restore();

        fetch('templates/template.html')
            .then(resp => resp.text())
            .then(data => {
                this.set('itemRenderer', data, {
                    forceRerender: false
                });
            });
    }
}

Rendering of a collection that contains any kind of object

It's not obligatory to fill an array with only Seemple instances, you can render any kind of object. Bindings for such objects can be defined using static method Seemple.bindNode.

class MyArray extends Seemple.Array {
    // Model is not given
    itemRenderer: ...
    onItemRender(item) {
        Seemple.bindNode(item, 'x', ':sandbox .some-node');
    }
})

One more little example: the rendering of a simple list:

class MyArray extends Seemple.Array {
    itemRenderer = '<li>{{value}}</li>';
    constructor() {
        super().bindNode('sandbox', '.my-list');
    }
});

const arr = new MyArray();
arr.push({ value: 'Item 1' }, { value: 'Item 2' });

Links

<virtual>Seemple.Array#trackBy: string

trackBy property indicates a key of unique IDs for collection items

In case if a client and a server activelly communicate with each other and collection items have unique IDs, then redrawing entire collection from scratch does not make sense. After the server respond with new collection, much better to check does the old collection contain objects which have the corresponding identifiers. If object ID from new collection matches object ID from old one, old object will be updated and used again. That means a new object (new instance of Seemple.Array#Model) will not be created and new DOM node will not be rendered.

trackBy works only using Seemple.Array#recreate because recreate is the only method which recreates collection from scratch.

In examples below _id key is used as identifier (you can use any other key).

class MyArray extends Seemple.Array {
    get trackBy() {
        return '_id';
    }
    constructor() {
        //...
    }
});

const arr = new MyArray();

// adds 3 objects to the collection
arr.recreate([
    {_id: 0, name: 'Foo'},
    {_id: 1, name: 'Bar'},
    {_id: 2, name: 'Baz'}
]);

// the next recreate call
// removes an object with _id: 0
// adds an object with _id: 3
// updates an object with _id: 3 (changes name from Bar to BarNew)
// updates an object with _id: 2 (changes name from Baz to BazNew)
// resorts collection
arr.recreate([
    {_id: 1, name: 'BarNew'},
    {_id: 3, name: 'Qux'},
    {_id: 2, name: 'BazNew'}
]);

trackBy can have "$index" value, allowing to update objects by their index in collection.

class MyArray extends Seemple.Array {
    get trackBy() {
        return '$index';
    }
    constructor() {
        //...
    }
});

const arr = new MyArray();

// adds 3 objects to the collection
arr.recreate([
    {name: 'Foo'},
    {name: 'Bar'},
    {name: 'Baz'}
]);

// the next recreate call
// updates all 3 items with new "name"
// adds an object with name "Qux"
arr.recreate([
    {name: 'NewFoo'},
    {name: 'NewBar'},
    {name: 'NewBaz'},
    {name: 'Qux'}
]);

Links

Seemple.Array.from(arrayLike, mapFn, thisArg) seempleArray

The function creates a new Seemple.Array instance from array-like or iterable object

Returns seempleArray

Arguments

Name Type Details
arrayLike object

Array-like or iterable object

mapFn optional function

The mapping function which is called for each element of an array

thisArg optional *

An object which is used as this on calling mapFn

Links

Examples

const seempleArray = Seemple.Array.from([1, 2, 3, 4]);
const seempleArray = Seemple.Array.from([1, 2, 3, 4], item => item * 2);

Inheritance of the method

class MyClass extends Seemple.Array {
    // ...
}

const myArray = MyClass.from([1, 2, 3, 4]);
console.log(myArray instanceof MyClass); // true

Seemple.Array.of() seempleArray

The function creates a new Seemple.Array instance with a variable number of arguments, regardless of number or type of the arguments

Returns seempleArray

Links

Examples

const seempleArray = Seemple.Array.of(1, 2, 3, 4);

Inheritance of the method

class MyClass extends Seemple.Array {
    // ...
}

const myArray = MyClass.of(1, 2, 3, 4);
console.log(myArray instanceof MyClass); // true

eventHandler: function

Event handler. Takes any arguments passed to Seemple#trigger

Arguments

Name Type Details
options * Any arguments

Examples

const eventHandler = (...args) => {
	console.log(args);
};
this.on('fyeah', eventHandler);
// logs 'foo', 'bar', 'baz'
this.trigger('fyeah', 'foo', 'bar', 'baz');

seemple: object

Seemple instance

Examples

const seemple = new Seemple();
obj.calc('a', 'b');

seempleObject: object

Seemple.Object instance

Examples

const obj = new Seemple.Object({ foo: 'x' });
obj.setData({ bar: 'y' });

seempleArray: object

Seemple.Array instance

Examples

const arr = new Seemple.Array(1, 2, 3);
arr.push(4);

eventNames: string

Event name or space-delimited list of event names.

Custom events.
this.on('myevent', () => {...});
this.trigger('myevent');
change:KEY which is triggered every time when a property is changed.
this.on('change:x', evt => {...});
this.x = 42;
beforechange:KEY which is triggered every time before a property is changed.
this.on('beforechange:x', evt => {...});
this.x = 42;
addevent:NAME and addevent which are triggered on event add.
// for any event
this.on('addevent', evt => {...});
// for "someevent" event
this.on('addevent:someevent', evt => {...});
// the line below fires "addevent" and "addevent:someevent"
this.on('someevent', evt => {...});
removeevent:NAME and removeevent which are triggered on event remove.
// for any event
this.on('removeevent', evt => {...});
// for "someevent" event
this.on('removeevent:someevent', evt => {...});
// the line below fires "removeevent" and "removeevent:someevent"
this.off('someevent', evt => {...});
DOM_EVENT::KEY, where DOM_EVENT is a name of DOM event, KEY is a key. A handler is called when DOM_EVENT is triggered on a node which is bound to the KEY.
this.bindNode('x', '.my-div');
this.on('click::x', evt => {
    alert('clicked ".my-div"');
});
DOM_EVENT::KEY(SELECTOR), where DOM_EVENT is a name of DOM event, KEY is a key, SELECTOR is a selector. A handler is called when DOM_EVENT is triggered on a node which matches the SELECTOR within a node bound to the KEY.
<div class="my-div">
    <button class="my-button"></button>
</div>
this.bindNode('x', '.my-div');
this.on('click::x(.my-button)', evt => {
    alert('clicked ".my-button"');
});
DOM_EVENT::(SELECTOR), where DOM_EVENT is a name of DOM event, SELECTOR is a selector. A handler is called when DOM_EVENT is triggered on a node which matches the SELECTOR within a sandbox.
this.bindNode('sandbox', '.my-div');
this.on('click::(.my-button)', evt => {
    alert('clicked ".my-button"');
});

The same as:

this.bindNode('sandbox', '.my-div');
this.on('click::sandbox(.my-button)', evt => {
    alert('clicked ".my-button"');
});
Delegated events: PATH@EVENT, where PATH is a path to a target object whose events we want to listen, EVENT is an event name.
this.on('a@someevent', () => {...});
this.on('a.b.c@change:d', () => {...});

If you need to listen an event of every item of Seemple.Array or every data property of Seemple.Object, you can use an asterisk "*" instead of specific property name.

this.on('*@someevent', () => {...});
this.on('*.b.*.d@change:e', () => {...});
Any combinations. All events described above can be combined.
this.on('x.y.z@click::(.my-selector)', () => {...});

binder: object

binder contains all information about how to synchronize instance property value with DOM node state. Every member of a binder uses HTML node as its context (this)

Properties

Name Type Details
on optional string function

DOM event (or space-delimited list of events) which tells when the node state is changed. Besides, it accepts a function as a value if you need to customize a listener definition

getValue optional function

A function which tells how to retrieve a value (state) from HTML node when DOM event is fired

setValue optional function

A function which tells how to change DOM node when the property value is changed

initialize optional function

A function which is called before binding is launched. For example it can initialize jQuery plugin or something else

destroy optional function

A function which is called when a binding is removed using unbindNode method

Examples

const binder = {
	on: 'click',
	getValue(bindingOptions) {
		return this.value;
	},
	setValue(v, bindingOptions) {
		this.value = v;
	},
	initialize(bindingOptions) {
		alert('A binding is initialized');
	},
	destroy(bindingOptions) {
		alert('A binding is destroyed');
	}
};

this.bindNode('a', '.my-checkbox', binder);
const binder = {
	on(callback, bindingOptions) {
		this.onclick = callback;
	},
	// ...
};
// ...

eventOptions: object

An object which can contain service flags or custom data which will be passed to an event handler

Examples

const eventOptions = { silent: true };

this.a = 1;

this.on('change:a', () => {
	alert('a is changed');
});

this.set('a', 2, eventOptions); // no alert
const eventOptions = { f: 'yeah' };

this.a = 1;

this.on('change:a', eventOptions => {
	alert(eventOptions.f);
});

this.set('a', 2, eventOptions); // alerts "yeah"

class: function

A class made using ECMAScript 2015 syntax or returned by Seemple.Class function

Examples

class MyClass {
	method() { ... }
};
const MyClass = Seemple.Class({
	method() { ... }
});

node

A DOM node

Examples

const node = document.querySelector('.foo');

$nodes

DOM nodes collection. For example, jQuery instance or NodeList.

Examples

let $nodes = $('.foo');
$nodes = document.querySelectorAll('.bar');

string

A string

Examples

const foo = 'bar';

boolean

A boolean

Examples

const bool = true;

number

A number

Examples

const num = 42;

object

An object

Examples

const obj = {
	foo: 'x',
	['bar']: 'y'
};

array

An array

Examples

const arr = ['foo', undefined, null, () => {}];

function

A function

Examples

function comeOnBarbieLetsGoParty() {
	alert("I'm a Barbie girl, in a Barbie world");
}

null

null

Examples

const x = null;

*

Any type

Examples

let whatever = 'foo';
whatever = 42;

FAQ

How does Seemple work?

Seemple uses accessors, setters in particular, for implementing the two-way data binding and catching the events of property changing.

As an example of how the two-way data binding works (bindNode function in particular), have a look at this code:

function bindNode(object, key, node, binder) {
    const value = object[key];
    Object.defineProperty(object, key, {
        get() {
            return value;
        },
        set(v) {
            binder.setValue.call(node, v);
        }
    });

    node.addEventListener(binder.on, () => {
        value = binder.getValue.call(node);
    });
}

As you see, it’s real easy (for simplicity, the function doesn’t support many-to-many binding).

How to pre-render an application on a server

For the app pre-rendering Seemple.js can be used on Node.js (window global object can be created via jsdom) or any template system you want on any server platform. The first case is fine for static HTML generation and the second for dynamic pages.

The task of client-side is to restore application state from HTML. Seemple#bindNode extracts element value and assigns it to a property and Seemple.Array#restore restores a state of a collection.

What is "debounce"

At this page you often can see the phrase "micropattern debounce". This is widely used pattern which enforces that a function not be called again until a certain amount of time has passed without it being called. More info can be found there.

How should look like a big application?

An application written with Seemple.js usually represents one nested JavaScript object where every branch is Seemple instance. The new branches are created using Seemple#instantiate which ensures the integrity of the application and allows to replace a state of the application using ordinary assignment.

How to render one object at few collections

The first thing: you need to set bindRenderedAsSandbox as false for an item class. It turns off automatic sandbox initialization for rendered object (two sandboxes for one object aren't allowed).

The second thing: when render event is fired, check where the object is rendered. Parent array can be get using parentArray property of the event object.

Example. You have User class and two collections UsersA and UsersB (their Seemple.Array#itemRenderer can be different). For both collections User is a model.

class User extends Seemple.Object {
    constructor() {
        super();

        this.bindRenderedAsSandbox = false;

        setInterval(() => {
            // when "name" is changed
            // both bound nodes also change
            this.name = Math.random();
        }, 5000);
    }
    onRender(evt) {
        const { parentArray, node } = evt;

        if(parentArray instanceof UsersA) {
            this.bindNode({
                // pseudo-sandbox creation for syntax sugar at selectors
                // (not required)
                nodeA: node,
                name: ':bound(nodeA) .name',
                email: ':bound(nodeA) .email',
            });
        } else if(parentArray instanceof UsersB) {
            this.bindNode({
                nodeB: node,
                name: ':bound(nodeB) .user-name',
            });
        }
    }
}

Actually there are many ways to solve this problem. For example bindings can be declared at classes of the collections (an array will listen to render event of inserted objects)...

Found a typo?
Select it and press
+
A typo