Q: When should I use subclass and when should work on an object?
Working directly on objects allows to directly access the state of an object and experiment with its methods. You are also able to directly modify the UI using morphic controls. This usually allows to develop applications faster than with the more abstract class concept.
On the other hand classes are great for generalizing behavior. For example, imagine you have written a BugTracker application directly with objects and want to create a Todo application. Possibly both applications/their UI will have shared logic. Using class inheritance will allow to more efficiently share the behavior and changes to a generic base class will affect both applications. For the BugTracker example this means to move certain methods out of the BugTracker object into some class.
Side note: We are working on a concept to allow prototypical inheritance, i.e. with that the generalization described above can also be reached using objects only.
Q: How do you create a subclass of a morph in LK with JS?
Select the following code and hit Ctrl/Cmd + d
lively.morphic.Morph.subclass('NewMorphClass',
'method category', {
initialize: function($super) {
$super(new lively.morphic.Shapes.Rectangle(new Rectangle(0,0,100,100)))
this.setFill(Color.red)
},
addMorph: function($super, morph) {
$super(morph);
},
newMethod: function() {},
});
morph.openInWorld()
NewMorphClass.addMethods({
newMethod: function() {},
})
To add static/class side methods use
Object.extend(NewMorphClass, {
staticMethod: function() {},
})
Q: How do you visually create and manipulate a morph?
See the video Modify a morph object visually and with a script editor.
Q: How do you visually change a morph (add/remove functions or add/remove/change positions of composition elements) so that the changes become visible to everyone who is using it? How to make UI changes visually (without the SCB) and propagate them to all users of this morph? What are the implications of such a change on the morph users own code (i.e. how we ensure consistency)?
Changes made at object level will not have an effect on copies (either direct copies or copies that were created using the PartsBin). Currently, only changes to the morph classes will affect all morph objects.
As mentioned in the first question there will be a concept for applying the changes of a morph prototype ("master morph") to morphs connected to this prototype.
There is probably no definitive solution that with prototypical change distribution existing applications will continue to work. A solution could be that objects are only updated on demand or that updates can be undone.
Another possibility to create a sharable abstraction is to define Layers using ContextJS and apply them to objects or classes.
Q: How do you create a new Part Bin item (programatically/visually)? What is the process from scratch to having it available in the Parts Bin?
Loading:
Visually: Click on the menu halo and choose "copy to PartsBin"
Pogramatically:
part = lively.PartsBin.getPart('Ellipse', 'PartsBin/NewWorld')
part.openInWorld()
part.setFill(Color.green)
Storing:
Visually: Click on the menu halu and choose "Copy to PartsBin"
Programatically:
// optPartsSpaceName can be nothing or a String that defines the PartsBin category ("PartsSpace")
// that the morph should go into, e.g. "PartsBin/Tools".
part.copyToPartsBin(optPartsSpaceName)
access meta data:
part.getPartsBinMetaInfo()
Q: How can I find about the type of an object and where it is defined?
For class instances you can do:
this.constructor.type // lively.morphic.Text -- not this is this very text morph
this.constructor.sourceModule // module(Global.lively.morphic.Core)
Q: How to delete properties and scripts (of instances)
$morph('Ellipse').addMorph // print it
$morph('Ellipse').hasOwnProperty('addMorph')
$morph('Ellipse').constructor.prototype.hasOwnProperty('addMorph')
Q: How to define and run tests?
// load the test module (defines TestCase class)
module('lively.TestFramework').load(true)
TestCase.subclass('MyNewTestCase', {
setUp: function() { alert('run before every test method') },
tearDown: function() { alert('run after every test method') },
testTryItOut: function() {
this.assertEqual(1, 2, 'this test will fail');
},
})
// run the test, alternatively use a test runner from the PartsBin
test = new MyNewTestCase()
test.runAll()
test.result
Q: How to debug using the Google Chrome Web browser?
- Most JS debuggers support the "debugger" statement as means for setting a breakpoint. Simply add this to where you would like to halt and inspect the control flow.
- The Chrome debugger will automatically halt when a "throw" statement is reached. Since this happens not only for errors but also for control structures it might make sense to disable the automatic break (see figure DebugOnBreakButton) when errors are occur.
Q: How to record screencast tutorials?
See the screencast video "Creating video tutorials" to the right. Then start from and modify
http://lively-kernel.org/repository/webwerkstatt/documentation/videoTutorials/template.xhtml
Q: How is the Lively code structured?
- modules
- function definitions
- class and object definitions
- methods
- world local changes
- doits, class and object definitions
- PartsBin objects
- added scripts (methods)
[More to come...]
Q: How does a Lively page "boot"?
Every XHTML page representing a Lively world includes one external script: <script src="lively/bootstrap.js">
The path to that script is adapted during serialization of that world.
The data of the page are in <meta id="LivelyJSONWorld"><![CDATA[{"id":0," .... This is a serialized version of the JavaScript objects that "live" on a page.
What the booting does is to load all the necessary modules to load that world. Then all objects in LivelyJSONWorld are deserialized and the world is started.
There is an optimization: On certain pages a file called combindedModules.js is loaded. This file is a statically generated file that bundles some of our core modules to speed up loading.
Q: What is the syntax for lively.bindings.connect?
lively.bindings.connect(sourceObj, sourceProp, targetObj, targetProp, options)
Options can be null or a JS object with the optional properties:
converter
a function with
params: newValue, oldValue
returns: a value that is passed to targetObj[targetProp]
updater
a function with
params: $upd, newValue, oldValue
returns: nothing
updater function can trigger targetObj[targetProp] by calling $upd with any number of arguments.
$upd is itself a function that represents the activation of the connection. If it's not called then the connection is not updated.
removeAfterUpdate
a boolean. If it's true the connection is disconnected after it was triggered once.
varMapping
An object that maps variable names to values that can then be used in converter and updater functions.
Two connections are equal iff their sourceObj, sourceProp, targetObj, targetProp and are all equal. If a connection is installed and an equal connection already exists the former connection is removed.
Use morph.attributeConnections for direct access to connections, i.e. explicitly remove a connection.
Be aware that if connect is used together with $morph("aMorphName") that your application might not work when copies of the application are present.
Q: How do I delete files and directories?
Either using the FileBrowser or programmatically with
new WebResource(url).del()
url should point to the file or directory you want to remove.
Q: How can I reset the state of a morph before copying it into the PartsBin?
We have the following convention: A part (morph) can have a method reset(). This method can reinitialize the state of the object. If such a method exists it is shown in the menu of that part and can be invoked manually by clicking on the menu item.
Q: How to see what changes happend in a file?
User the version differ from the context menu of the module in the System Browser.
Q: How can objects be resized?
Use morph.setExtent(Point)
Q: How can I define a simple layout policy when an object is resized?
See the example:
panel = lively.morphic.Morph.makeRectangle(0,0, 100, 100)
panel.openInWorld()
child1 = lively.morphic.Morph.makeRectangle(10,10, 20, 20)
panel.addMorph(child1)
child2 = lively.morphic.Morph.makeRectangle(70,70, 20, 20)
panel.addMorph(child2)
panel.applyStyle({adjustForNewBounds: true})
child1.applyStyle({resizeWidth: true, resizeHeight: true})
child2.applyStyle({moveVertical: true, moveHorizontal: true})
panel.setExtent(panel.getExtent().addXY(3,3))