-
Notifications
You must be signed in to change notification settings - Fork 1
burtonsamograd/px2
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
PX2 - a Backbone style framework -------------------------------- PX2 is an MV*, Backbone-like framework with a more consistent design: - objects are also containers, of type Model or View - named getters and setters, including loud getters - containers can be specialized so they only contain a specific type of object and also contain members - Views derived from Model, so they can have members and getter/setters and be containers - objects that 'contain' other objects (by using 'set', 'add', 'push' or 'insertAt' methods) are passed messages from the objects they contain, just like Backbone collections but more general. - iterators (start,end,next,prev,current), standard mapping functions (map, each, forIn) and sorting. If you don't like the fact you can't have collections of collections or that views and collections can't have settable members in Backbone, then PX2 is for you. Installation ------------ npm install --save px2 bower install --save px2 Dependencies ------------ jQuery if you use Views, none otherwise. Examples -------- See the examples/ folder. Model methods ------------- this.create(name, value, silent): Creates a new member variable called 'name' with initial value of 'value'. Also creates named getters/setters, this.name() and this.name(value). Sends 'create' and 'create:$name' messages with value as event.value unless silent is true. this.copy(): Returns a deep copy of this. this.set(name, value, silent) Standard setter for Model objects. Sends "change" and "change:$name" events when called, unless silent is true. The value of this.name() is the new value and the event.value (passed as the first part of 'change' trigger handler) is the old value that this.name() was set to. this.get(name, loud) Standard getter for Model objects, sends 'get' and 'get:$name' messages. Will send 'get' and 'get:$name' messages if 'loud' is true, with the event value being the previous value of this.name(). This message is sent before the value is returned allowing the opportuntity to change the value that is going to be returned to the calling function. This allows for the creation of named variables that return a different value everytime they are called: // Create a random variable obj.create('random'); obj.on('get:random', function (e) { return this.random(Math.random()); }); var a = obj.get('random', true); expect(obj.get('random', true)).to.not.equal(a); this.add(value, silent) Add value to the collection if it is not already in the collection and sends "add" with event.target being the value added and "modified" with event.target being an array with the value added, unless silent is true. this.insertAt(i, value, silent) Add value to the collection at index i. Sends "add" with event.target being the value added and "modified" with event.target being an array with the value added, unless silent is true. this.push(value, silent) Add value to the head of the collection. Sends "add" with event.target being the value added and "modified" with event.target being an array with the value added, unless silent is true. Much faster than add, since it doesn't have to scan the entire storage looking for a duplicate. this.swap(i, j, silent) Exhanges two values in the collection. Sends "modified" with event.target being an array with the values swapped, unless silent is true. this.remove(value, silent) Remove value from the collection if it has been added to it using add/push/insertAt. Sends "add" with event.target being the value added and "modified" with event.target being an array with the value added, unless silent is true. Returns true if the object was removed, false otherwise. this.at(index) Return an element in the collection located at index. Throws Error if index is out of range. this.indexOf(obj) Return the integer index of obj in storage (for use with at), or undefined if the object is not in the collection. this.length: The number of elements contained in the object storage. this.clear(silent) Remove all items from the collection and sets length to 0; sends "modified" message with all items from storage before clearing, unles silent is true. this.on(message, function (event) { ... }, self) Intercept message with handler, setting this to self during call of handler. If 'self' is omitted, 'this' is used. 'event' contains event.value, which is the value passed to trigger, and event.target which will be the object that triggered the message, in most cases 'this'. If the event handler returns true, messages will continue to propagate up the containment chain, else it will stop at the handling object. this.once(message, function (event) { ... }, self) Like this.on, but the handler is only called once, after which it is removed from the handler action table. this.trigger(message, value) Send the message to this, and then all containing parents, recursively up the containment tree. Propagation stops when the message is intercepted and handled, unless the handler returns true (and only true, not truthy). this.each(function (value) { ... }, self) Side effects only iteration over elements. 'self' defaults to 'this' if not provided. this.map(function (value) {...}, self) Returns an array of returned values from function. 'self' defaults to 'this' if not provided. this.forIn(function (key, value) { ... }, self) Side effects only mapping over members (created with this.create()); 'self' defaults to 'this' if not provided. this.find(object || function (value) ...) Find a specific object in the collection. If the argument is an object, === is used, if a function it is called with each element of the collection until it returns true. Returns the object, if it is found. this.sort(fun, silent) Destructively sorts the internal storage array using fun, returns 'this' so you can chain with map. Sends "modified" event with the sorted array, unless silent is true. Iterators --------- All iterators send "change" and "change:current" messages unless silent is true, with the event.value being the previous value of this.current(). Note: the pattern for iterators is slightly different than other languages: for(var i = this.start(); i; i = this.next()) { ... } or in reverse: for(var i = this.end(); i; i = this.prev()) { ... } this.start(silent) Select the first element of storage as current. this.end(silent) Select the last element of storage as current. this.current(objOrNumber, silent) Return the current element with no aruments. With obj as an argument, select that object as the current object. With a number, select the object at that index in storage as current. this.next(loop, silent) Return the next element and set it as current, or undefined if at the end of storage. If loop is true, loop around to the start and return the first element. this.prev(loop, silent) Return the previous element and set it as current, or undefined if at the start of storage. If loop is true, loop around to the end and return the last element. Events ------ An event is passed to a trigger handler. An event includes the value and target fields; value is the value passed to set/add/push/etc, and target is the object that initiated the event. Serialization ------------- Serialization is working but some parts are experiemntal, so don't expect it to work perfectly yet with all/complex Models/Views. Try it at first with simple objects that have only members and storage and not complex actions (trigger that set setf to something other than this) See test/serialize.js for more info on what works and what doesn't.. this.serialize() Return an object containing all members, storage and actions that can be possibly passed to JSON.stringify() for storage. this.load(object, load-actions) Load 'object' into this. 'Object' should be something that was returned from this.serialize(), probably after being JSONified and back. If load actions is false, actions are not loaded into this, hopefully keeping them from getting trashed during the load (needs more testing and experimentation). Example of defining and using a Model as an object and a container ------------------------------------------------------------------ var Model = require("px2").Model; // Declare object class data var Point = Model({ // Optional, but good for debugging and containers type: 'Point' // optional, create members and set default values defaults: { system: "cartesian" }, // Constructor, called during instatiation init: function (x, y) { this.create('x', x); this.create('y', y); } // A random method toString: function () { return "x: " + this.x() + ", y: " + this.y() + " " + this.system(); } }) // Delcare a container var Points = Model({ type: 'Points', // This container contatins type... contains: 'Point' init: function () { this.create('currentPoint'); // undefined initially this.on('add', function (e) { // Use a named setter to set the current value of member 'currentPoint'. // e is passed to all trigger functions and it contains: // e.value: the value that was added/removed/updated/etc // e.target: the object that was updated, in this case it would be 'this' this.currentPoint(e.value); }); this.on('remove', function (e) { if(e.value === this.currentPoint()) { this.currenPoint(undefined); } }); } }); // Instantiate a Points collection var points = new Points(); points.add(new Point(100, 200)); var point = points.at(0); var xs = points.map(function (point) { return point.x(); }); // => [100] Views ----- Views are derived Model and contain all features and methods that Models give. View example ------------ var PointsView = View({ // optional, but handy for debugging and is the default class if // className is not defined (below) type: 'PointsView', // Set the name of the variable used to reference the model, // in this case this.points rather than this.model model "points" // optional, sets default tag name of this.$el, else is "div" tagName: "span", // optional, sets default class of this.$el. If not provided // the class name is set the the value of type (above). If the // string starts with a space, the provided class name(s) will be // appended to the value of type. ie: a value of className: " window" // will result in a final class name of "PointsView window" // in this case. className: " window", // optional, name for this.model. In this case, you could use // this.points to reference your model. The model is not // enclosed in the object, so messages from the model are // not propagated into this View. model: 'points', // optional css initial style, using jquery syntax style: { "font-weight": "bold" } // events, just like backbone events: { 'click': function (e) { alert(this.message()); // 'this' is bound for you automagically } } init: function (model, value) { this.create('value', value); this.create('message', "Hello World!"); // eithefq this.model or this.points can be used, due to // the 'model' option used above this.points.on('add', this.render); // 'this' is bound automagically // this.render() is automatically called at the end of init for Views. } // render, which if you don't provide a default is given that // just returns this.$el render: { return this.$el.html(this.points.map(function (v) { return $('<div>').text(v.toString()); })); } }) var points = new Points(); // A View constructor is similar to a Model constructor, but the // first argument is *always* the model for the view. var pointsView = new PointsView(points, 1); $('document').ready(function () { $('body').html(pointsView.$el); }); WTF is this written in? ----------------------- PX2 is written in Parenscript: https://github.com/vsedach/Parenscript To build from source you need sigil: https://github.com/burtonsamograd/sigil -- Burton Samograd <burton.samograd@gmail.com> Twitter: @kruhft 2015
About
PX2 is a Javascript Front End Framework designed to replace Backbone written in Parenscript.
Resources
Stars
Watchers
Forks
Packages 0
No packages published