Lively Kernel canvas
// Author: Philip Weaver
// Email: philip@philmaker.com
// Comments in right margin might not line up unless using Verdana 10 Bold on Mac OS X, TextWrangler
// I am getting more comfortable with Lively and I need to review and tidy up a bit
// particularly how I may sometimes be incorrectly passing bogus shapes, rectangles, extents, and positions
// also I know I should probably be using styling notation more than calling style methods - I'll fix that later
//Config.modulesOnWorldLoad.push('GridLayout.js');
Object.subclass('philmaker.foundation.Utility', {
initialize : function($super) {
$super();
}
});
philmaker.foundation.Utility.getSubmorphByClass = function(morph, clazz) { // could instead use Morph.addMethods
// use Namespace.addMethods ???
var result = null;
morph.withAllSubmorphsDo(function() { // not sure what the second parameter 'rest' does (does it rest?)
if (result == null) {
if (this instanceof clazz) {
result = this;
}
}
});
return result;
};
philmaker.foundation.Utility.getSubmorphsByClass = function(morph, clazz) {
var results = [];
morph.withAllSubmorphsDo(function() {
if (this instanceof clazz) {
results.push(this);
}
});
return results;
};
philmaker.foundation.Utility.getSubmorphByName = function(morph, name) {
var result = null;
morph.withAllSubmorphsDo(function() {
if (result == null) {
if ((this.name) && (this.name == name)) {
result = this;
}
}
});
return result;
};
philmaker.foundation.Utility.getOpenLocation = function() {
var point = WorldMorph.current().lastMenuLocation;
if (! point) {
var point = WorldMorph.current().firstHand().getPosition();
}
return point;
};
philmaker.foundation.Utility.getSelectedMorphs = function() {
var results = [];
var world = WorldMorph.current();
world.withAllSubmorphsDo(function() {
if (this instanceof SelectionMorph) {
var selectionMorph = this;
selectionMorph.selectedMorphs.forEach(function(morph) {
results.push(morph);
});
}
});
return results;
};
philmaker.foundation.Utility.createCodeNoteWithFunctionBody = function(func, comments) { // todo: consider looking up from the Class browser instead
var source = func.toString();
if (true) {
if (true) {
source = source.replace('function () \n{', '');
} else {
source = source.replace(new RegExp('function\s*\(\)\s*'), '');
}
source = source.substr(0, source.length - 2);
//source = source.replace('\n ', '\nxxxxx', ['g']);
source = source.replace(new RegExp('\n.{2}', ['g']), '\n'); // instead of . should be able to use /s ???
var pattern = new RegExp('\n.*{', ['g']);
if (false) {
pattern = new RegExp('\s*\n.*{', ['g']); // prefer to correct the space padding - for example, appearing after 'try'
}
source = source.replace(pattern, ' {');
}
var codeNote = new philmaker.playground.RunnableCodeNote();
var textMorph = philmaker.foundation.Utility.getSubmorphByClass(codeNote, TextMorph);
var text = source;
if (comments) {
text = comments + text;
}
textMorph.setTextString(text);
return codeNote;
};
philmaker.foundation.Utility.revertWorldToPreviousRevision = function(filename) { // example: pweaver-widgets.xhtml
// http://livelykernel.sunlabs.com/repository/lively-wiki/!svn/bc/REVISIONID/PAGE-NAME.xhtml
var revisions = philmaker.foundation.Utility.findRevisions(filename);
var revision = revisions[1];
console.log('previous revision for: ' + filename + ' is: ' + revision);
philmaker.foundation.Utility.revertToRevision(filename, revision);
console.log('finished reverting file');
};
philmaker.foundation.Utility.findRevisions = function(filename) {
var url = URL.source.getDirectory();
var flieDirectory = new FileDirectory(url);
var svnResource = new SVNResource(url.toString(), Record.newPlainInstance({
URL : url + filename,
Metadata : null,
HeadRevision : null
}));
svnResource.fetchMetadata(true, null);
var timestamps = svnResource.getMetadata();
var revisions = timestamps.collect(function(each) {
return each.toString().match(/.*Revision (.*)/)[1]
});
return revisions;
};
philmaker.foundation.Utility.revertToRevision = function(filename, revision) {
var flieDirectory = new FileDirectory(URL.source);
var content = flieDirectory.fileContent(filename, revision);
flieDirectory.writeFileNamed(filename, content);
};
TextMorph.subclass('philmaker.foundation.TextMorph', { // todo: allow setting of font family alternates
initialize : function($super, rectangle, text) {
$super(rectangle, text);
},
setBold : function(bold) {
var style = 'unbold';
if (bold) {
style = 'bold';
}
this.setSelectionRange(0, this.textString.length);
this.emphasizeBoldItalic({
style : style
});
this.setSelectionRange(0, 0);
},
setAlignment : function(alignment) {
try {
console.log('philmaker.foundation.TextMorph.setAlignment');
this.setSelectionRange(0, this.textString.length);
this.emphasizeSelection({
align : alignment
});
} catch (e) {
console.log('philmaker.foundation.TextMorph.setAlignment error: ' + e);
}
},
/*
setAlignment : function(alignment) {
try {
console.log('philmaker.foundation.TextMorph.setAlignment');
console.log('alignment: ' + alignment);
console.log('this.textString: ' + this.textString);
this.setSelectionRange(0, this.textString.length);
console.log('after setSelectionRange');
if (this.hasNullSelection()) { // next several lines of code are from TextMorph.emphasizeSelection
return;
}
var text = new lively.Text.Text(this.textString, this.textStyle); // changed thisModule.Text to lively.Text.Text (from TextMorph.emphasizeSelection)
text.emphasize(alignment, 0, this.textString.length - 2);
this.textStyle = text.style;
//this.composeAfterEdits();
} catch (e) {
console.log('philmaker.foundation.TextMorph.setAlignment error: ' + e);
}
},
*/
onKeyPress : function($super, event) { // todo: I actually need to patch TextMorph.onKeyPress
// investigate being able to patch with a wrapper around the original method
if (! this.acceptInput) return true;
var result = $super(event);
if (! event.isMetaDown() && ! event.isCtrlDown()) return false;
if (event.getKeyChar().toLowerCase() === 'a' || event.getKeyCode() === 97) { // todo: also handle undo/redo
if (false) {
this.doSelectAll(true); // I don't like this behavior inside doSelectAll: this.charsTyped.length
} else { // so I'm not using it
this.setSelectionRange(0, this.textString.length);
}
event.stop();
return true;
} else {
return result;
}
},
isDoubleClick : function() {
},
onMouseDown : function($super, evt) { // fixing in my subclass first and later will patch TextMorph mouse handlers
// goal is double-click drag word accumulation and triple-click drag line accumulation
var lastClick = this.lastClick;
this.lastClick = new Date();
$super(evt);
if (lastClick != null) {
if (false) {
console.log('this click: ' + this.lastClick.getTime());
console.log('last click: ' + lastClick.getTime());
console.log('delta: ' + (this.lastClick.getTime() - lastClick.getTime()));
}
if (this.lastClick.getTime() - lastClick.getTime() < 300) {
if (this.selectionRange[0] == 0 || this.selectionRange[0] == this.textString.length) {
this.setSelectionRange(0, this.textString.length);
} else {
this.selectionRange = this.locale.selectWord(this.textString, this.selectionRange[0]);
this.selectionPivot = this.selectionRange[0];
}
this.setSelection(this.getSelectionString());
this.drawSelection();
}
}
return true;
},
extendSelectionEvt : function(evt) {
var charIx = this.charOfPoint(this.localize(evt.mousePoint));
if (charIx < 0) {
return;
}
var oldSelectionRange = this.selectionRange;
var oldSelectionPivot = this.selectionPivot;
var selectionRange = this.locale.selectWord(this.textString, charIx);
this.setSelectionRange(oldSelectionPivot, selectionRange[1] + 1); // fixme: haven't studied why I'm needing + 1 (pjw)
},
onMouseUp : function(evt) {
this.isSelecting = false;
if ((this.selectionRange[1] != this.selectionRange[0] - 1) ||
(this.priorSelection[1] != this.priorSelection[0] - 1) ||
(this.selectionRange[0] != this.priorSelection[0]) ) {
this.previousSelection = this.priorSelection;
return;
}
}
});
Morph.subclass('philmaker.foundation.BasicMorph', { // just a base class for fixed morphs
// not yet employed - just created this class as a reminder
initialize : function($super, shape) {
$super(shape);
this.openForDragAndDrop = false;
this.suppressHandles = true;
this.setFillOpacity(0.0);
this.setStrokeOpacity(0.0);
}
});
Object.subclass('philmaker.foundation.Insets', {
initialize : function(top, left, bottom, right) {
this.top = top;
this.left = left;
this.bottom = bottom;
this.right = right;
}
});
BoxMorph.subclass('philmaker.foundation.BoxLayoutMorph', { // fixme: need to convert this into a LayoutManager
// this is sort of quick and dirty because I can't get existing layouts to do exactly I want
openForDragAndDrop : false, // fixme: need to provide clipping or submorph resizing; example: so TextMorphs do not extend too far
initialize : function($super, rectangle) {
$super(rectangle);
this.orientation = 'horizontal';
this.suppressHandles = true;
this.setFillOpacity(0.0);
this.setBorderWidth(0);
},
setOrientation : function(orientation) {
this.orientation = orientation;
},
addMorph : function($super, morph, constraints) { // so I can pass in constaints
if (constraints) {
morph.constraints = constraints;
this.packageConstraints(morph);
}
return $super(morph);
},
adjustForNewBounds : function ($super) { // fixme: this should actually all be in a LayoutManager
// allow for passing integers instead of decimals (for fixed width)
$super(); // fixme: this does not appear to resize correctly from corners other that southeast
// also need to prevent TextMorphs from overlapping
var position = this.getPosition();
var extent = this.getExtent();
var i = 0;
var offset = 0;
var boxLayoutMorph = this;
this.submorphs.forEach(function(submorph) {
boxLayoutMorph.unpackageConstraints(submorph);
});
if (this.orientation == 'vertical') { // fixme: need to combine vertical and horizontal handling into one abstract case
var flexibleExtent = extent.y; // also make sure features of each case are in sync before doing so
this.submorphs.forEach(function(submorph) {
if (submorph.constraints) {
var weight = submorph.constraints.weight;
if (weight > 1) {
flexibleExtent = flexibleExtent - weight;
}
}
});
this.submorphs.forEach(function(submorph) {
if (submorph.constraints) {
var weight = submorph.constraints.weight;
var margin = submorph.constraints.margin;
var alignX = submorph.constraints.alignX;
var existingExtent = submorph.getExtent();
var newPosition = pt(margin.left, offset + margin.top);
submorph.setPosition(newPosition); // IMPORTANT: need to heed the preferred height of the morph above it (TextMorph)
var calculatedExtent;
if (weight <= 1) {
calculatedExtent = pt(extent.x - (margin.left + margin.right), (flexibleExtent * weight) - (margin.top + margin.bottom));
} else {
calculatedExtent = pt(extent.x - (margin.left + margin.right), weight - (margin.top + margin.bottom));
}
if (i == 1) {
calculatedExtent.y = calculatedExtent.y - 1; // so the lower handle is usable, FIXME: need to do this for the last not the first
} // actually need to make sure that all corners are grabable
if (alignX == 'center') { // need to adjust the margins (but center within the margins)
var delta = (calculatedExtent.x - existingExtent.x) / 2;
newPosition.x = newPosition.x + delta;
submorph.setPosition(newPosition);
submorph.setExtent(calculatedExtent);
} else {
submorph.setExtent(calculatedExtent);
}
calculatedExtent = submorph.getExtent();
offset = offset + calculatedExtent.y + margin.top + margin.bottom;
i++;
}
});
} else {
var flexibleExtent = extent.x;
this.submorphs.forEach(function(submorph) {
if (submorph.constraints) {
var weight = submorph.constraints.weight;
if (weight > 1) {
flexibleExtent = flexibleExtent - weight;
}
}
});
this.submorphs.forEach(function(submorph) {
if (submorph.constraints) {
var weight = submorph.constraints.weight;
var margin = submorph.constraints.margin;
margin = new philmaker.foundation.Insets(margin.top, margin.left, margin.bottom, margin.right);
var alignX = submorph.constraints.alignX;
var existingExtent = submorph.getExtent();
var newPosition = pt(offset + margin.left, margin.top);
submorph.setPosition(newPosition);
var calculatedExtent;
if (weight <= 1) {
calculatedExtent = pt((flexibleExtent * weight) - (margin.left + margin.right), extent.y - (margin.top + margin.bottom));
} else {
// does not position correctly with a final fixed element : situation was RunnableCodeNoteControls
calculatedExtent = pt(weight - (margin.left + margin.right), extent.y - (margin.top + margin.bottom));
}
if (i == 1) {
calculatedExtent.x = calculatedExtent.x - 1; // so the lower handle is draggable, FIXME: should do this for the last not the first
} // might actually need to inset on each side (or some other Lively API solution)
if (alignX == 'center') { // need to adjust the margins (but center within the margins)
var delta = (calculatedExtent.x - existingExtent.x) / 2;
newPosition.x = newPosition.x + delta;
submorph.setPosition(newPosition);
submorph.setExtent(calculatedExtent);
} else {
submorph.setExtent(calculatedExtent);
}
offset = offset + calculatedExtent.x + margin.left + margin.right;
i++;
}
});
}
},
packageConstraints : function(morph) {
var constraints = morph.constraints;
var margin = morph.constraints.margin;
if (! margin) {
margin = new philmaker.foundation.Insets(0, 0, 0, 0);
}
constraints.margin = {
top : margin.top,
left : margin.left,
bottom : margin.bottom,
right : margin.right,
}
morph.layoutConstraints = JSON.serialize(constraints);
},
unpackageConstraints : function(morph) {
if (morph.layoutConstraints) {
morph.constraints = JSON.unserialize(morph.layoutConstraints);
}
}
});
Object.subclass('philmaker.foundation.BoxLayoutConstraints', { // object with chainable setter methods
initialize : function() {
}
});
Object.subclass('philmaker.foundation.GridLayoutConstraints', { // object with chainable setter methods
initialize : function() {
}
});
ImageMorph.subclass('philmaker.foundation.ImageMorph', { // provide an interface for changing the image source and hyperlinks url
// better = inspector which adds function interface
initialize : function($super, rectangle, imagePath) { // select function and it populates (last issued command on that function somehow)
$super(rectangle, imagePath);
this.setFillOpacity(0.0);
}
});
Morph.subclass('philmaker.foundation.RatingMorph', { // be able to customize the number of stars/bounds: maybe initially 1-20 max
// do an overlay perhaps 5 of 10
openForDragAndDrop: false, // investigate inspection by turnover = or maybe a drawer sliding out (actually why would this need inspection anyway?)
reshape : Functions.Null, // idea: allow partial fill of each star to act more like a slider
styleClass: [],
initialize : function($super) {
var rectangle = new Rectangle(400, 400, 236, 26);
$super(new lively.scene.Rectangle(rectangle)); // how to set/reset this after calling super?
this.setMaximum(10);
var color = Color.hsb(0.0, 0.0, 0.5);
this.setFill(color);
this.setFillOpacity(0.8);
this.shapeRoundEdgesBy(13);
this.litColor = Color.hsb(60, 1.0, 0.9);
this.unlitColor = Color.white;
var x = 8;
var y = 3;
var maximum = this.getMaximum();
for (var i = 0; i < maximum; i++) {
var ratingStarMorph = new philmaker.foundation.RatingStarMorph(i);
ratingStarMorph.setPosition(pt(x, y));
this.addMorph(ratingStarMorph);
x = x + 22;
}
this.setValue(1);
},
setMaximum : function(maximum) {
this.maximum = maximum;
},
getMaximum : function() {
return this.maximum;
},
setValue : function(value) {
this.value = value;
this.updateStars(this.value);
},
setTemporaryValue : function(temporaryValue) {
this.temporaryValue = temporaryValue;
this.updateStars(this.temporaryValue);
},
updateStars : function(value) {
this.submorphs.forEach(function(submorph) {
if (submorph.index < value) {
submorph.setLit(true);
} else {
submorph.setLit(false);
}
});
},
setLitColor : function(litColor) {
this.litColor = litColor;
this.setValue(this.value);
},
setUnlitColor : function(unlitColor) {
this.unlitColor = unlitColor;
this.setValue(this.value);
},
onMouseOut: function($super, event) {
this.setValue(this.value);
}
});
Morph.subclass('philmaker.foundation.RatingStarMorph', {
suppressHandles : true,
openForDragAndDrop : false,
styleClass : [],
handlesMouseDown : Functions.True,
initialize : function($super, index) {
this.index = index;
var vertices = this.createVertices(10, pt(0, 0), 180); // fixme: need to use radians correctly
$super(new lively.scene.Polygon(vertices));
this.setLit(false);
},
createVertices : function(radius, center, startAngle) {
var vertices = [];
var verticeCount = 10;
for (var i = 0; i <= verticeCount; i++) {
var a = startAngle + (2 * Math.PI / verticeCount * i);
var p = Point.polar(radius, a);
if (i % 2 == 0) {
p = p.scaleBy(0.45);
}
vertices.push(p.addPt(center));
}
return vertices;
},
setLit : function(lit) {
this.lit = lit;
if (this.owner) {
if (this.lit) {
this.setFill(this.owner.litColor);
} else {
this.setFill(this.owner.unlitColor);
}
}
},
isLit : function() {
return this.lit;
},
onMouseOver: function($super, event) {
this.owner.setTemporaryValue(this.index + 1);
},
onMouseDown : function($super, event) {
this.owner.setValue(this.index + 1);
},
reshape: Functions.Null
});
Morph.subclass('philmaker.foundation.InstantiationWidget', { // simple text field for making classes - it should list history (or initially just use up and down)
// make sure to use ... ? what?
openForDragAndDrop: false, // also float inside a shiny black round rect - actually use green for GO
styleClass: [], // use the word make in the widget
suppressHandles : true,
// pre-populate the history
initialize: function($super) {
// still place where the hand is - try to start drag
var rectangle = new Rectangle(150, 10, 500, 26); // or at least place somewhere non-covered
$super(new lively.scene.Rectangle(rectangle));
this.initializeHistory();
var color = Color.hsb(0.0, 0.0, 0.5);
color = Color.rgb(51, 212, 103);
this.setFill(color);
this.setFillOpacity(0.8);
this.shapeRoundEdgesBy(13);
this.label = new TextMorph(new Rectangle(0, 0, 50, 30), 'Create:');
this.label.beLabel();
this.label.setFontSize(16);
this.label.setTextColor(Color.white);
this.label.setFillOpacity(0.0);
this.label.setStrokeOpacity(0.0);
this.addMorph(this.label);
this.instantiationTextMorph = new philmaker.foundation.InstantiationTextMorph(this);
this.addMorph(this.instantiationTextMorph);
this.shouldCycleHistory = false;
},
initializeTransientState: function($super) {
$super();
this.initializeHistory();
},
initializePersistentState: function($super, shape) {
$super(shape);
this.initializeHistory();
},
initializeHistory : function() {
this.history = [
'philmaker.playground.WormholeMorph',
'philmaker.playground.BlogEntry',
'philmaker.playground.StructuredContent',
'philmaker.playground.ImageMorph',
'philmaker.playground.RatingMorph',
'philmaker.foundation.InstantiationWidget',
'philmaker.playground.StickyNote',
'philmaker.playground.ScrollableStickyNote',
'philmaker.playground.CodeNote',
'philmaker.playground.RunnableCodeNote'
];
this.historyIndex = 0;
},
nextHistoryItem : function() {
if (this.historyIndex < (this.history.length - 1)) {
this.historyIndex++;
this.instantiationTextMorph.setTextString(this.history[this.historyIndex]);
}
},
previousHistoryItem : function() {
if (this.historyIndex > 0) {
this.historyIndex--;
this.instantiationTextMorph.setTextString(this.history[this.historyIndex]);
}
},
isHistoryItem : function(string) {
if (this.history.indexOf(string) > -1) {
return true;
} else {
return false;
}
},
addHistoryItem : function(string) {
if (this.isHistoryItem(string)) {
this.removeHistoryItem(string);
}
this.history.unshift(string);
this.historyIndex = 0;
},
removeHistoryItem : function(string) {
var sReturn = string;
var index = this.history.indexOf(string);
if (index > -1) {
var string = this.history[index];
this.history.splice(index, 1);
sReturn = string;
} else {
sReturn = null;
}
return sReturn;
},
instantiateClass : function(string) {
try {
string = 'WorldMorph.currentWorld.addMorph(new ' + string + '());';
eval(string);
} catch (e) {
console.log('class instantiation failed');
console.log(e);
}
},
reshape: Functions.Null
});
philmaker.foundation.TextMorph.subclass('philmaker.foundation.InstantiationTextMorph', { // also use autocomplete
openForDragAndDrop: false,
styleClass: [],
suppressHandles : true,
initialize: function($super, instantiationWidget) {
this.instantiationWidget = instantiationWidget;
var rectangle = new Rectangle(70, 5, 420, 20);
$super(rectangle, this.instantiationWidget.history[0]);
this.setStrokeOpacity(0.0);
this.setFillOpacity(0.0);
this.setTextColor(Color.white);
this.setFontSize(16);
if (false) {
this.hasKeyboardFocus = false;
this.beInputLine(50);
this.autoAccept = true;
}
this.focusHaloBorderWidth = 0;
this.applyStyle({
borderWidth : 0,
fill : null,
wrapStyle : lively.Text.WrapStyle.None,
fontSize : 16,
padding : Rectangle.inset(0)
});
},
onKeyDown: function($super, event) {
var key = event.getKeyCode() || event.charCode;
if (key == Event.KEY_DOWN) {
this.instantiationWidget.nextHistoryItem();
event.stop();
} else if (key == Event.KEY_UP) {
this.instantiationWidget.previousHistoryItem();
event.stop();
} else if (key == Event.KEY_RETURN) {
var text = this.textString;
this.instantiationWidget.addHistoryItem(text);
this.instantiationWidget.instantiateClass(text);
event.stop();
} else {
$super(event);
}
},
reshape: Functions.Null
});
Morph.subclass('philmaker.foundation.CommandHistoryWidget', { // InstantiationWidget and PerformActionWidget ought to extend from this
initialize: function($super) {
$super();
}
});
Morph.subclass('philmaker.foundation.PerformActionWidget', {
initialize: function($super) {
$super();
}
});
LinkMorph.subclass('philmaker.foundation.LinkMorph', { // need to integrate WormholeMorph
// consider "Edit Name" in MorphMenu
// this code has been archived
});
var text = '';
text = text + 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, ';
text = text + 'sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ';
text = text + 'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. ';
text = text + 'Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. ';
text = text + 'Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.';
philmaker.foundation.sampleText = text;
philmaker.foundation.layoutBorders = false;
namespace('philmaker.actions');
Object.subclass('philmaker.actions.BasicAction', {
enabled : false,
performAction : function() {
console.log('BasicAction.performAction');
}
});
philmaker.actions.BasicAction.subclass('philmaker.actions.RevertWorld', {
name : 'Revert Any World',
enabled : true,
initialize : function() {
},
performAction : function() {
var codeNote = new philmaker.playground.RunnableCodeNote();
var textMorph = philmaker.foundation.Utility.getSubmorphByClass(codeNote, TextMorph);
textMorph.setTextString('philmaker.foundation.Utility.revertWorldToPreviousRevision(\'pweaver-xxxxx.xhtml\')');
var point = philmaker.foundation.Utility.getOpenLocation();
codeNote.setPosition(point);
var extent = codeNote.getExtent();
extent.y = 70;
codeNote.setExtent(extent);
WorldMorph.current().addMorph(codeNote);
}
});
philmaker.actions.BasicAction.subclass('philmaker.actions.ViewChangeSet', {
name : 'View ChangeSet',
enabled : true,
initialize : function() {
},
performAction : function() {
require('lively.ide').toRun(function() {
console.log('ViewChangeSet.performAction lively.ide.SystemBrowser');
var browser = new lively.ide.SystemBrowser();
var point = philmaker.foundation.Utility.getOpenLocation();
browser.openIn(WorldMorph.current(), point);
browser.inPaneSelectNodeNamed('Pane1', 'ChangeSet for World');
try {
browser.inPaneSelectNodeNamed('Pane2', 'preamble');
} catch (e) {
console.log('Exception in ViewChangeSet.performAction (possibly no preamble to select)');
}
});
}
});
philmaker.actions.BasicAction.subclass('philmaker.actions.AlterWorldExtent', { // I know I should not be interacting with the canvas directly
// the alternative here is to use the scrollpanes which Lively provides
name : 'Alter World Extent',
enabled : true,
width : null, // 1440
height : 2000,
initialize : function() {
},
performAction : function() {
try {
this.alterWorldExtent();
} catch (e) {
console.log(e);
}
},
alterWorldExtent : function() {
var canvas = WorldMorph.current().canvas();
if (canvas) {
if ((this.width) && (this.width != null)) {
canvas.setAttribute('width', new String(this.width));
}
if ((this.height) && (this.height != null)) {
canvas.setAttribute('height', new String(this.height));
}
if (true) {
var width = canvas.getAttribute('width');
var height = canvas.getAttribute('height');
var extent = WorldMorph.current().getExtent();
if (! width.endsWith('%')) {
extent.x = width;
}
if (! height.endsWith('%')) {
extent.y = height;
}
this.overrideWorldMorphBounds(); // essential
var world = WorldMorph.current();
world.setExtent(extent);
}
}
},
convertPercentageToPixels: function() {
},
overrideWorldMorphBounds : function() {
WorldMorph.prototype.bounds = function() {
var extent = this.getExtent();
return new Rectangle(0, 0, extent.x, extent.y);
};
// overriding WorldMorph.bounds (only) to return the extent instead. This is the call stack:
// HandMorph.reallyHandleMouseEvent
// Morph.morphToReceiveEvent
// Morph.morphToGrabOrReceive
// Morph.fullContainsWorldPoint
// Morph.fullContainsPoint
// Morph.bounds (WorldMorph is returning the incorrect bounds even after calling setBounds)
}
});
philmaker.actions.BasicAction.subclass('philmaker.actions.MakeToolboxArray', {
name : 'Make Toolbox Array',
enabled : true,
initialize : function() {
},
performAction : function() {
var point = philmaker.foundation.Utility.getOpenLocation();
this.makeStack(point.copy());
point.x = point.x + 110;
this.makeStack(point);
},
makeStack : function(point) {
for (var i = 0; i < 5; i++) {
var morph = new philmaker.playground.ShelfBox();
morph.setPosition(point);
WorldMorph.current().addMorph(morph);
point.y = point.y + 110;
}
}
});
philmaker.actions.BasicAction.subclass('philmaker.actions.MakeRecordArray', {
name : 'Make Record Array',
enabled : true,
initialize : function() {
},
performAction : function() {
var specs = [{
kind : 'Task',
title : 'Take out the Trash',
rating : 8,
tags : ['Chores', 'Incomplete']
}, {
kind : 'Task',
title : 'Feed the Dog',
rating : 10,
tags : ['Chores', 'Complete']
}, {
kind : 'Task',
title : 'Water the Plants',
rating : 4,
tags : ['Chores', 'Incomplete']
}, {
kind : 'Task',
title : 'Fix the Car',
rating : 9,
tags : ['Errands', 'Incomplete']
}, {
kind : 'Task',
title : 'Buy More RAM',
rating : 2,
tags : ['Errands', 'Complete']
}, {
kind : 'Task',
title : 'Get a Haircut',
rating : 6,
tags : ['Errands', 'Incomplete']
}, {
kind : 'Contact',
title : 'Bullwinkle Moose',
rating : 1,
tags : ['Fictional'],
fields : [{
name : 'address',
value : 'Frostbite Falls, Minnesota'
}, {
name : 'phone',
value : '(218) 555-1212'
}]
}];
var point = philmaker.foundation.Utility.getOpenLocation();
for (var i = 0; i < specs.length; i++) {
var spec = specs[i];
var record = new philmaker.playground.RecordMorph(spec);
record.setPosition(point.copy());
WorldMorph.current().addMorph(record);
point.y = point.y + record.getExtent().y + 5;
}
}
});
philmaker.actions.BasicAction.subclass('philmaker.actions.MakeBadges', {
name : 'Make Badges',
enabled : true,
initialize : function() {
},
performAction : function() {
var specs = [{
text : 'Lively: Making the World Rounder',
enabled : true
}, {
text : 'Lively: Shape Your World',
enabled : true
}, {
text : 'Lively: Back to the Future',
enabled : true
}];
var point = philmaker.foundation.Utility.getOpenLocation();
for (var i = 0; i < specs.length; i++) {
var spec = specs[i];
if (spec.enabled) {
var badge = new philmaker.playground.BadgeMorph(spec.text);
record.setPosition(point.copy());
WorldMorph.current().addMorph(record);
point.y = point.y + record.getExtent().y + 5;
}
}
}
});
philmaker.actions.BasicAction.subclass('philmaker.actions.RunnableCodeNoteAction', {
name : 'Runnable Code Note Action',
enabled : true,
initialize : function() {
},
performAction : function() {
var codeNote = philmaker.foundation.Utility.createCodeNoteWithFunctionBody(this.script, this.comments());
var point = philmaker.foundation.Utility.getOpenLocation();
codeNote.setPosition(point);
var extent = codeNote.getExtent();
extent.y = 300;
codeNote.setExtent(extent);
WorldMorph.current().addMorph(codeNote);
},
comments : function() {
return null;
},
script : function() {
;
}
});
philmaker.actions.RunnableCodeNoteAction.subclass('philmaker.actions.ArrangeRecordsByRating', {
name : 'Arrange Records By Rating',
enabled : true,
initialize : function() {
},
performAction : function($super) {
if (true) {
$super();
} else {
this.script();
}
},
comments : function() {
return '// ' + this.name + '\n';
},
script : function() {
var records = philmaker.playground.RecordAssistant.findAllRecords();
records.sort(function(record1, record2) {
var rating1 = philmaker.foundation.Utility.getSubmorphByClass(record1, philmaker.foundation.RatingMorph).value;
var rating2 = philmaker.foundation.Utility.getSubmorphByClass(record2, philmaker.foundation.RatingMorph).value;
if (rating1 > rating2) return -1;
if (rating1 < rating2) return 1;
if (rating1 == rating2) return 0;
});
var position = records[0].getPosition();
records.forEach(function(record) {
position.x = Math.min(position.x, record.getPosition().x);
position.y = Math.min(position.y, record.getPosition().y);
});
records.forEach(function(record) {
var title = philmaker.foundation.Utility.getSubmorphByName(record, 'title').textString;
record.setPosition(pt(position.x, position.y));
position.y = position.y + record.getExtent().y + 4;
});
}
});
philmaker.actions.RunnableCodeNoteAction.subclass('philmaker.actions.ArrangeRecordsByCompleted', {
name : 'Arrange Records By Completed',
enabled : true,
initialize : function() {
},
performAction : function($super) {
if (true) {
$super();
} else {
this.script();
}
},
comments : function() {
return '// ' + this.name + '\n';
},
script : function() {
var getStatusCode = function(record) {
var tags = philmaker.foundation.Utility.getSubmorphsByClass(record, philmaker.playground.TagMorph);
for (var i = 0; i < tags.length; i++) {
var value = tags[i].getValue();
if (value == 'Complete') {
return 2;
} else if (value == 'Incomplete') {
return 1;
}
}
return 0;
};
var records = philmaker.playground.RecordAssistant.findAllRecords();
records.sort(function(record1, record2) {
var status1 = getStatusCode(record1);
var status2 = getStatusCode(record2);
if (status1 > status2) return -1;
if (status1 < status2) return 1;
if (status1 == status2) return 0;
});
var position = records[0].getPosition();
records.forEach(function(record) {
position.x = Math.min(position.x, record.getPosition().x);
position.y = Math.min(position.y, record.getPosition().y);
});
records.forEach(function(record) {
var title = philmaker.foundation.Utility.getSubmorphByName(record, 'title').textString;
record.setPosition(pt(position.x, position.y));
position.y = position.y + record.getExtent().y + 4;
});
}
});
philmaker.actions.RunnableCodeNoteAction.subclass('philmaker.actions.ModifyBlogEntry1', {
name : 'Modify Blog Entry (Script 1)',
enabled : true,
initialize : function() {
},
comments : function() {
return '';
},
script : function() {
var selectedMorphs = philmaker.foundation.Utility.getSelectedMorphs();
selectedMorphs.forEach(function(selectedMorph) {
if (selectedMorph instanceof philmaker.playground.BlogEntry) {
var blogEntry = selectedMorph;
try {
var date = new Date();
date.setYear(2012);
blogEntry.setPostDate(date);
blogEntry.setTitle('Hey Look: Its Flipper!');
blogEntry.setImageUrl('http://www.marocga.com/albums/userpics/normal_dolphin11.jpg');
blogEntry.addTag('Ocean Life');
} catch (e) {
console.log(e);
}
}
}, this);
}
});
philmaker.actions.RunnableCodeNoteAction.subclass('philmaker.actions.ModifyBlogEntry2', {
name : 'Modify Blog Entry (Script 2)',
comments : function() {
return '';
},
script : function() {
var selectedMorphs = philmaker.foundation.Utility.getSelectedMorphs();
selectedMorphs.forEach(function(selectedMorph) {
if (selectedMorph instanceof philmaker.playground.BlogEntry) {
var blogEntry = selectedMorph;
try {
var date = new Date();
blogEntry.setPostDate(date);
blogEntry.setTitle('Dog Days of Summer');
blogEntry.setImageUrl('http://russian.wunderground.com/data/wximagenew/t/thib/61.jpg');
blogEntry.removeTag('Ocean Life');
} catch (e) {
console.log(e);
}
}
}, this);
}
});
philmaker.actions.BasicAction.subclass('philmaker.actions.ShelfBoxDemo', {
name : 'ToolBox Demo',
enabled : true,
initialize : function() {
},
performAction : function() {
var point = pt(100, 100);
for (var i = 0; i < 5; i++) {
var morph = new philmaker.playground.ShelfBox();
morph.setPosition(point);
WorldMorph.current().addMorph(morph);
point.y = point.y + 110;
}
var point = pt(400, 400);
for (var i = 0; i < 5; i++) {
WorldMorph.current().addMorph(Morph.makeRectangle(point, pt(60, 30)));
point.y = point.y + 50;
}
}
});
philmaker.actions.BasicAction.subclass('philmaker.actions.PrintSubmorphPositions', {
name : 'Print Submorph Positions',
description : 'Print the positions of the morph and submorphs of the selection in the selection tray. ' +
'This can be useful when working locally without persistence to be able to drag arrange morphs then encode those positions.',
enabled : true,
initialize : function() {
},
performAction : function() {
var selectedMorphs = philmaker.foundation.Utility.getSelectedMorphs();
selectedMorphs = selectedMorphs.reverse();
selectedMorphs.forEach(function(selectedMorph, index) {
this.printBounds('selected morph ', index, selectedMorph);
selectedMorph.submorphs.forEach(function(submorph, index2) {
this.printBounds(' submorph ', index2, submorph);
}, this);
}, this);
},
printBounds : function(description, index, morph) {
console.log(description + index + ' position: ' + morph.getPosition() + ' extent: ' + morph.getExtent());
}
});
philmaker.actions.BasicAction.subclass('philmaker.actions.InstallCustomWorldMorphMenus', {
name : 'Install Custom WorldMorph Menus',
enabled : true,
initialize : function() {
},
performAction : function() {
var self = this;
var morphMenuOriginal = WorldMorph.prototype.morphMenu;
WorldMorph.prototype.morphMenu = function($super, event) { // I haven't looked at Class.addMixin
var morphMenuOriginal = arguments.callee.morphMenuOriginal; // but for now I'm overriding WorldMorph.morphMenu with a new function
var menuMorph = morphMenuOriginal.apply(this, arguments); // and attaching the original morphMenu function to it
menuMorph.addLine(); // fixme: review doing this with an actual closure instead
self.addComponentsSubMenu(menuMorph);
self.addActionSubMenu(menuMorph);
return menuMorph;
}
WorldMorph.prototype.morphMenu.morphMenuOriginal = morphMenuOriginal;
},
addActionSubMenu : function(menuMorph) {
console.log('philmaker.actions.RunnableCodeNoteAction.type: ' + philmaker.actions.RunnableCodeNoteAction.type);
var excludes = [
philmaker.actions.RunnableCodeNoteAction,
philmaker.actions.InstallCustomWorldMorphMenus,
philmaker.actions.InstallMenuLocationMemory,
philmaker.actions.InstallColorPatch,
philmaker.actions.InstallPathPatch,
philmaker.actions.BasisAction
];
var actions = [];
for (var i in philmaker.actions) { // install the namespace of actions automatically
if (philmaker.actions.hasOwnProperty(i)) {
var value = philmaker.actions[i];
if (value.prototype instanceof philmaker.actions.BasicAction) {
var include = true;
for (var i = 0; i < excludes.length; i++) {
var exclude = excludes[i];
if (value == exclude) {
include = false;
break;
}
}
if (include) {
actions.push(new value());
}
}
}
}
for (var i = 0; i < actions.length; i++) {
var action = actions[i];
var func = function() {
var action = arguments.callee.action;
return action.performAction.apply(action, arguments);
}
func.action = action;
actions[i] = [
action.name,
func
];
}
var submenuDef = ['Custom Actions', actions];
menuMorph.addItem(submenuDef);
},
addComponentsSubMenu : function(menuMorph) {
var components = [
philmaker.playground.ScrollableStickyNote,
philmaker.playground.RunnableCodeNote,
philmaker.playground.RatingMorph,
philmaker.playground.TagMorph,
philmaker.playground.RecordMorph,
philmaker.playground.BlogEntry,
philmaker.playground.StructuredContent,
philmaker.playground.ShelfBox,
philmaker.playground.WormholeMorph,
philmaker.playground.ImageMorph,
philmaker.foundation.InstantiationWidget,
philmaker.playground.StickyNote,
philmaker.playground.CodeNote
];
for (var i = 0; i < components.length; i++) {
var component = components[i];
var func = function() {
var component = arguments.callee.component; // fixme: review doing this with an actual closure instead
component = new component();
var point = philmaker.foundation.Utility.getOpenLocation();
WorldMorph.current().addMorphAt(component, point);
}
func.component = component;
var name = component.type;
var index = name.lastIndexOf('.');
if (index > -1) {
name = name.substring(index + 1);
}
components[i] = [
name,
func
];
}
var submenuDef = ['Custom Components', components];
menuMorph.addItem(submenuDef);
},
addSubMenu : function(menuMorph, name, items) {
for (var i = 0; i < items.length; i++) {
var item = items[i];
var func = function() {
var action = arguments.callee.action;
return action.performAction.apply(action, arguments);
}
func.action = item;
items[i] = [
item.name,
func
];
}
var submenuDef = [name, items];
menuMorph.addItem(submenuDef);
}
});
philmaker.actions.BasicAction.subclass('philmaker.actions.InstallMenuLocationMemory', { // could be using the ChangeSet instead
name : 'Install Menu Location Memory',
enabled : true,
initialize : function() {
},
performAction : function() {
var self = this;
var showMorphMenuOriginal = Morph.prototype.showMorphMenu;
Morph.prototype.showMorphMenu = function(event) {
this.world().lastMenuLocation = event.point(); // fixme: review doing this with an actual closure instead
var showMorphMenuOriginal = arguments.callee.showMorphMenuOriginal;
return showMorphMenuOriginal.apply(this, arguments);
}
Morph.prototype.showMorphMenu.showMorphMenuOriginal = showMorphMenuOriginal;
}
});
philmaker.actions.BasicAction.subclass('philmaker.actions.InstallColorPatch', {
name : 'Install Color Patch',
performAction : function() {
Color.addMethods({
toLiteral : function() {
return {
r : this.r,
g : this.g,
b : this.b
}
}
});
}
});
philmaker.actions.BasicAction.subclass('philmaker.actions.InstallPathPatch', {
name : 'Install lively.scene.Path Patch',
enabled : true,
initialize : function() {
},
performAction : function() {
lively.scene.Path.addMethods({
copy : function($super) {
var result = $super();
result.elements = this.elements;
return result;
}
});
}
});
philmaker.actions.BasicAction.subclass('philmaker.actions.BasisAction', {
name : 'Basis Action',
enabled : true,
initialize : function() {
},
performAction : function() {
console.log('BasisAction.performAction');
}
});
function startup(worldMorph) {
new philmaker.playground.LocalInitializer();
}
/*
Object.extend(Global.document, {
oncontextmenu : function(event) {
console.log('oncontextmenu');
return false;
}.logErrors('Context Menu Handler')
});
*/
Object.subclass('philmaker.playground.WorldInitializer', {
initialize : function() {
console.log('WorldInitializer.initialize');
},
initializePreamble : function() {
TextSelectionMorph.prototype.style.fill = Color.rgb(255, 204, 83);
try {
SimpleInspector.prototype.initialViewExtent = pt(700, 400);
} catch (e) {
console.log(e);
}
try {
InspectorWidget.prototype.initialViewExtent = pt(700, 400);
} catch (e) {
console.log(e);
}
var actions = [{
clazz : philmaker.actions.InstallCustomWorldMorphMenus,
run : true
}, {
clazz : philmaker.actions.InstallMenuLocationMemory,
run : true
}, {
clazz : philmaker.actions.InstallColorPatch, // todo: make sure to report this
run : true
}, {
clazz : philmaker.actions.InstallPathPatch,
run : true
}];
for (var i = 0; i < actions.length; i++) {
var action= actions[i];
if (action.run) {
var clazz = action.clazz;
action = new clazz();
if (action instanceof philmaker.actions.BasicAction) {
action.performAction();
}
}
}
},
initializePostscript : function() {
if (false) {
var action = new philmaker.actions.AlterWorldExtent();
action.performAction();
}
}
});
Object.subclass('philmaker.playground.LocalInitializer', {
initialize : function() {
console.log('LocalInitializer.initialize');
var contenders = [{
clazz : philmaker.playground.ScrollableStickyNote,
instantiate : false
}, {
clazz : philmaker.playground.RunnableCodeNote,
instantiate : false
}, {
clazz : philmaker.playground.RatingMorph,
instantiate : false
}, {
clazz : philmaker.playground.TagMorph,
instantiate : false
}, {
clazz : philmaker.playground.RecordMorph,
instantiate : false
}, {
clazz : philmaker.playground.BlogEntry,
instantiate : true
}, {
clazz : philmaker.playground.StructuredContent,
instantiate : false
}, {
clazz : philmaker.playground.ShelfBox,
instantiate : false
}, {
clazz : philmaker.playground.WormholeMorph,
instantiate : false
}, {
clazz : philmaker.playground.ImageMorph,
instantiate : false
}, {
clazz : philmaker.foundation.InstantiationWidget,
instantiate : false
}, {
clazz : philmaker.playground.StickyNote,
instantiate : false
}, {
clazz : philmaker.playground.CodeNote,
instantiate : false
}, {
clazz : philmaker.playground.BoxLayoutMorph,
instantiate : false
}, {
clazz : MetamorphosisDemo,
instantiate : false
}];
if (true) {
for (var i = 0; i < contenders.length; i++) {
var contender = contenders[i];
if (contender.instantiate) {
var clazz = contender.clazz;
var object = new clazz();
if (object instanceof Morph) {
var morph = object;
WorldMorph.current().addMorph(morph);
}
}
}
}
if (false) {
var morph = new philmaker.playground.RatingMorph();
morph.setFill(Color.blue);
morph.setLitColor(Color.white);
morph.setUnlitColor(Color.blue);
WorldMorph.current().addMorph(morph);
morph.setValue(4);
}
if (false) {
var action = new philmaker.actions.TweakBlogEntry();
action.performAction();
}
}
});
philmaker.foundation.RatingMorph.subclass('philmaker.playground.RatingMorph', {
initialize : function($super) {
$super();
this.setPosition(pt(200, 400));
}
});
Object.subclass('philmaker.playground.BlogPage', { // for mindshare, need to make a page which is indistinguishable from a regular hypertext document
// do this with BlogPage or create a class StructuredContent
initialize: function($super) {
$super();
}
});
philmaker.foundation.BoxLayoutMorph.subclass('philmaker.playground.BlogEntry', {
openForDragAndDrop : true,
initialize : function($super) {
$super(new Rectangle(50, 50, 700, 220));
this.setOrientation('horizontal');
this.setFill(Color.white);
this.setFillOpacity(1.0);
this.setBorderColor(Color.hsb(0.0, 0.0, 0.97));
this.setBorderWidth(3);
this.shapeRoundEdgesBy(15);
this.suppressHandles = false;
this.blogEntryDateMorph = new philmaker.playground.BlogEntryDateMorph();
this.center = new philmaker.playground.BlogEntryBody();
this.center.padding = Rectangle.inset(5, 5, 5, 5);
var rectangle = new Rectangle(0, 0, 180, 180);
var imagePath = 'http://russian.wunderground.com/data/wximagenew/t/thib/61.jpg';
this.blogImageMorph = new ImageMorph(rectangle, imagePath);
this.blogImageMorph.setFillOpacity(0.0);
this.blogImageMorph.rotateBy(-0.05);
this.addMorph(this.blogEntryDateMorph, {
weight : 100,
margin : new philmaker.foundation.Insets(20, 10, 25, 5)
});
this.addMorph(this.center, {
weight : 1,
margin : new philmaker.foundation.Insets(20, 5, 25, 5)
});
this.addMorph(this.blogImageMorph, {
weight : 200,
margin : new philmaker.foundation.Insets(20, 5, 25, 10)
});
if (philmaker.foundation.layoutBorders) {
this.setBorderWidth(1);
this.setBorderColor(Color.red);
}
this.adjustForNewBounds(); // hack for now
},
setPostDate : function(date) {
var blogEntryDateMorph = philmaker.foundation.Utility.getSubmorphByClass(this, philmaker.playground.BlogEntryDateMorph);
blogEntryDateMorph.setDate(date);
},
getPostDate : function() {
return null;
},
setTitle : function(title) {
var textMorph = philmaker.foundation.Utility.getSubmorphByName(this, 'title');
textMorph.setSelectionRange(0, textMorph.textString.length);
textMorph.replaceSelectionWith(title); // todo: investigate: setTextString etc cause freezes when the text is bold
},
getImageUrl : function() {
return null;
},
setImageUrl : function(url) {
var imageMorph = philmaker.foundation.Utility.getSubmorphByClass(this, ImageMorph);
imageMorph.loadFromURL(url);
},
getImageUrl : function() {
return null;
},
addTag : function(tagText) {
var tagsPanel = philmaker.foundation.Utility.getSubmorphByClass(this, philmaker.playground.BlogEntryTagsPanel);
tagsPanel.addTag(tagText);
},
removeTag : function(tagText) {
var tagsPanel = philmaker.foundation.Utility.getSubmorphByClass(this, philmaker.playground.BlogEntryTagsPanel);
tagsPanel.removeTag(tagText);
},
prepareSampleText : function() {
var result = '';
for (var i = 0; i < 1; i++) {
result = result + philmaker.foundation.sampleText + '\n\n';
}
return result;
}
});
philmaker.foundation.BoxLayoutMorph.subclass('philmaker.playground.BlogEntryBody', {
initialize : function($super) {
$super(new Rectangle(50, 50, 600, 400));
this.setOrientation('vertical');
this.setFillOpacity(0.0);
this.setBorderWidth(0);
var rectangle = new Rectangle(0, 0, 400, 20);
var titleTextMorph = new philmaker.playground.BlogEntryTextMorph(rectangle, 'Dog Days of Summer', 'title');
titleTextMorph.setBold(true);
titleTextMorph.setFontSize(18);
titleTextMorph.padding = Rectangle.inset(0, 0, 0, 0); // loses padding after font size change
var bodyTextMorph = new philmaker.playground.BlogEntryTextMorph(rectangle, philmaker.foundation.sampleText, 'body');
var tagsPanel = new philmaker.playground.BlogEntryTagsPanel();
this.addMorph(titleTextMorph, {
weight : .25,
margin : new philmaker.foundation.Insets(2, 0, 5, 0)
});
this.addMorph(bodyTextMorph, {
weight : 0.5,
margin : new philmaker.foundation.Insets(5, 0, 5, 0)
});
this.addMorph(tagsPanel, {
weight : .25,
margin : new philmaker.foundation.Insets(15, 0, 0, 0)
});
if (philmaker.foundation.layoutBorders) {
this.setBorderWidth(1);
this.setBorderColor(Color.red);
}
this.adjustForNewBounds(); // hack for now
}
});
Morph.subclass('philmaker.playground.BlogEntryTagsPanel', {
openForDragAndDrop : true,
initialize : function($super) {
$super(new lively.scene.Rectangle(new Rectangle(50, 50, 600, 400)));
this.setFillOpacity(0.0);
this.setBorderWidth(0);
var tagMorph = new philmaker.playground.TagMorph();
tagMorph.setValue('Photos');
tagMorph.setPosition(pt(0, 0));
this.addMorph(tagMorph);
},
addTag : function(tagText) {
var rightmost = 0;
this.withAllSubmorphsDo(function() {
if (this instanceof philmaker.playground.TagMorph) { // do we care about instanceof?
var position = this.getPosition();
var extent = this.getExtent();
var thisRightmost = position.x + extent.x;
rightmost = Math.max(rightmost, thisRightmost);
}
});
var tagMorph = new philmaker.playground.TagMorph();
tagMorph.setValue(tagText);
tagMorph.setPosition(pt(rightmost + 10, 0));
this.addMorph(tagMorph); // todo: maybe first arrange them tidy as well
},
removeTag : function(tagText) {s
this.withAllSubmorphsDo(function() {
if (this instanceof philmaker.playground.TagMorph) {
var tagMorph = this;
if (tagMorph.getValue() == tagText) {
console.log('removing tag');
tagMorph.remove();
}
}
});
}
});
philmaker.foundation.TextMorph.subclass('philmaker.playground.BlogEntryTextMorph', {
suppressHandles : true,
openForDragAndDrop : false,
styleClass : [],
initialize : function($super, rectangle, text, name) {
$super(rectangle, text);
this.name = name;
this.setFillOpacity(0.0);
this.setBorderWidth(0);
this.setTextColor(Color.hsb(0.0, 0.0, 0.4));
this.focusHaloBorderWidth = 0;
this.padding = Rectangle.inset(0, 0, 0, 0);
if (philmaker.foundation.layoutBorders) {
this.setBorderWidth(1);
this.setBorderColor(Color.red);
}
}
});
philmaker.foundation.BoxLayoutMorph.subclass('philmaker.playground.BlogEntryDateMorph', {
initialize : function($super) {
$super(new Rectangle(0, 0, 100, 300));
this.setOrientation('vertical');
this.setFill(Color.white);
this.setFillOpacity(0.0);
this.setBorderColor(Color.hsb(0.0, 0.0, 0.9));
this.setBorderWidth(0);
this.shapeRoundEdgesBy(0);
var rectangle = new Rectangle(0, 0, 100, 20);
this.monthTextMorph = new philmaker.playground.BlogEntryDateTextMorph(rectangle, 'month');
this.monthTextMorph.setFontSize(24);
this.monthTextMorph.padding = Rectangle.inset(0, 0, 0, 0);
this.dayTextMorph = new philmaker.playground.BlogEntryDateTextMorph(rectangle, 'day');
this.dayTextMorph.setFontSize(24);
this.dayTextMorph.padding = Rectangle.inset(0, 0, 0, 0);
this.yearTextMorph = new philmaker.playground.BlogEntryDateTextMorph(rectangle, 'year');
this.yearTextMorph.setFontSize(24);
this.yearTextMorph.padding = Rectangle.inset(0, 0, 0, 0);
this.addMorph(this.monthTextMorph, {
weight : 20,
margin : new philmaker.foundation.Insets(0, 0, 0, 0),
alignX : 'center'
});
this.addMorph(this.dayTextMorph, {
weight : 20,
margin : new philmaker.foundation.Insets(0, 0, 0, 0),
alignX : 'center'
});
this.addMorph(this.yearTextMorph, {
weight : 20,
margin : new philmaker.foundation.Insets(0, 0, 0, 0),
alignX : 'center'
});
if (philmaker.foundation.layoutBorders) {
this.setBorderWidth(1);
this.setBorderColor(Color.red);
}
this.setDate(new Date());
this.adjustForNewBounds(); // hack for now
},
setDate : function(date) {
console.log('BlogEntryDateMorph.setDate: ' + date);
var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; // fixme: localize, refactor these months of the instance
var month = date.getMonth();
month = months[month];
var day = date.getDate();
day = new String(day);
var year = date.getFullYear();
year = new String(year);
this.monthTextMorph.setSelectionRange(0, this.monthTextMorph.textString.length); // woraround for bolded text
this.monthTextMorph.replaceSelectionWith(month);
this.dayTextMorph.setSelectionRange(0, this.dayTextMorph.textString.length);
this.dayTextMorph.replaceSelectionWith(day);
this.yearTextMorph.setSelectionRange(0, this.yearTextMorph.textString.length);
this.yearTextMorph.replaceSelectionWith(year);
}
});
philmaker.foundation.TextMorph.subclass('philmaker.playground.BlogEntryDateTextMorph', {
suppressHandles : true,
openForDragAndDrop : false,
styleClass : [],
initialize : function($super, rectangle, name) {
$super(rectangle, ' ');
this.myName = name;
this.setFillOpacity(0.0);
this.setBorderWidth(0);
this.setTextColor(Color.hsb(0.0, 0.0, 0.85));
this.focusHaloBorderWidth = 0;
this.beLabel(); // can cause GridLayout alignment difficulty
//this.setAlignment('center');
this.setBold(true);
this.setFontSize(14);
if (philmaker.foundation.layoutBorders) {
this.setBorderWidth(1);
this.setBorderColor(Color.red);
}
}
});
philmaker.foundation.BoxLayoutMorph.subclass('philmaker.playground.StructuredContent', {
initialize : function($super) {
$super(new Rectangle(10, 50, 700, 450));
this.setOrientation('vertical');
this.setFill(Color.white);
this.setFillOpacity(1.0);
this.setBorderColor(Color.hsb(0.0, 0.0, 0.9));
this.setBorderWidth(1);
this.shapeRoundEdgesBy(0);
this.suppressHandles = false;
var rectangle = new Rectangle(0, 0, 150, 500);
this.top = new philmaker.playground.StructuredContentBar();
this.center = new philmaker.playground.StructuredContentHorizontal();
this.center.padding = Rectangle.inset(5, 5, 5, 5);
this.addMorph(this.top, {
weight : 46,
margin : new philmaker.foundation.Insets(10, 10, 0, 10)
});
this.addMorph(this.center, {
weight : .6,
margin : new philmaker.foundation.Insets(5, 0, 0, 0)
});
if (philmaker.foundation.layoutBorders) {
this.setBorderWidth(1);
this.setBorderColor(Color.red);
}
this.adjustForNewBounds(); // hack for now
}
});
philmaker.foundation.BoxLayoutMorph.subclass('philmaker.playground.StructuredContentBar', {
initialize : function($super) {
$super(new Rectangle(0, 0, 0, 0));
this.setOrientation('horizontal');
var hue = 216;
var saturation = 0.35;
hue = 0;
saturation = 0.0;
this.setFill(new lively.paint.LinearGradient(
[
new lively.paint.Stop(0, Color.hsb(hue, saturation, 0.79)),
new lively.paint.Stop(0.45, Color.hsb(hue, saturation, 0.72)),
new lively.paint.Stop(0.65, Color.hsb(hue, saturation, 0.65)),
new lively.paint.Stop(0.85, Color.hsb(hue, saturation, 0.58)),
new lively.paint.Stop(1, Color.hsb(hue, saturation, 0.51))
], lively.paint.LinearGradient.NorthSouth
));
this.setFillOpacity(1.0);
this.setBorderColor(Color.hsb(0.0, 0.0, 0.7));
this.setBorderWidth(0);
this.shapeRoundEdgesBy(10);
var titles = ['One', 'Two', 'Three', 'Four', 'Five', 'Six'];
for (var i = 0; i < titles.length; i++) {
this.button = new philmaker.playground.StructuredContentBarButton(titles[i]);
this.button.padding = Rectangle.inset(5, 5, 5, 5);
var weight = 1 / titles.length;
this.addMorph(this.button, {
weight : weight,
margin : new philmaker.foundation.Insets(0, 0, 0, 0)
});
}
this.adjustForNewBounds(); // hack for now
}
});
philmaker.foundation.BoxLayoutMorph.subclass('philmaker.playground.StructuredContentBarButton', { // on mouse over, increase fill opacity
// using BoxLayoutMorph really just for centering of
initialize : function($super, text) { // the nested text component
$super(new Rectangle(0, 0, 0, 0));
this.setFillOpacity(0.0);
this.setBorderWidth(0);
this.shapeRoundEdgesBy(0);
var textMorph = new philmaker.foundation.TextMorph(new Rectangle(0, 0, 0, 0), text);
textMorph.setFillOpacity(0.0);
textMorph.beLabel();
textMorph.setFontFamily('Optima');
textMorph.setFontSize(16);
textMorph.padding = Rectangle.inset(0, 0, 0, 0);
this.addMorph(textMorph, {
weight : 1.0,
margin : new philmaker.foundation.Insets(10, 0, 0, 0),
alignX : 'center'
});
this.adjustForNewBounds(); // hack for now
},
onMouseOver : function() {
this.setFillOpacity(0.1); // fixme: need corner clipping
},
onMouseOut : function() {
this.setFillOpacity(0.0);
}
});
philmaker.foundation.BoxLayoutMorph.subclass('philmaker.playground.StructuredContentHorizontal', {
initialize : function($super) {
$super(new Rectangle(100, 50, 800, 650));
this.setOrientation('horizontal');
this.setFill(Color.white);
this.setFillOpacity(1.0);
this.setBorderColor(Color.hsb(0.0, 0.0, 0.9));
this.setBorderWidth(0);
this.shapeRoundEdgesBy(0);
var rectangle = new Rectangle(0, 0, 150, 500);
this.leftTextMorph = new philmaker.playground.StructuredContentTextMorph(rectangle, this.prepareSidebarText(), 'left');
this.leftTextMorph.setFontFamily('Optima');
this.leftTextMorph.padding = Rectangle.inset(5, 5, 5, 5);
this.center = new philmaker.playground.StructuredContentInner();
this.center.padding = Rectangle.inset(5, 5, 5, 5);
this.rightTextMorph = new philmaker.playground.StructuredContentTextMorph(rectangle, this.prepareSidebarText(), 'right');
this.rightTextMorph.setFontFamily('Optima');
this.rightTextMorph.padding = Rectangle.inset(5, 5, 5, 5);
this.addMorph(this.leftTextMorph, {
weight : 0.2,
margin : new philmaker.foundation.Insets(10, 10, 10, 5)
});
this.addMorph(this.center, {
weight : 0.6,
margin : new philmaker.foundation.Insets(10, 5, 10, 5)
});
this.addMorph(this.rightTextMorph, {
weight : 0.2,
margin : new philmaker.foundation.Insets(10, 5, 10, 10)
});
if (philmaker.foundation.layoutBorders) {
this.setBorderWidth(1);
this.setBorderColor(Color.red);
}
this.adjustForNewBounds(); // hack for now
},
prepareSidebarText : function() {
var result = '';
for (var i = 0; i < 20; i++) {
result = result + 'Sidebar Link' + '\n';
}
return result;
}
});
philmaker.foundation.BoxLayoutMorph.subclass('philmaker.playground.StructuredContentInner', {
initialize : function($super) {
$super(new Rectangle(0, 0, 100, 300));
this.setOrientation('vertical');
this.setFill(Color.white);
this.setFillOpacity(1.0);
this.setBorderColor(Color.hsb(0.0, 0.0, 0.9));
this.setBorderWidth(0);
this.shapeRoundEdgesBy(0);
var rectangle = new Rectangle(0, 0, 150, 500);
this.headerTextMorph = new philmaker.playground.StructuredContentTextMorph(rectangle, 'Lively: The Web Done Right', 'header');
this.headerTextMorph.setFontFamily('Optima');
this.headerTextMorph.setFontSize(24);
//this.headerTextMorph.setBold(true); // bug: bold kerning freaks out
this.headerTextMorph.padding = Rectangle.inset(5, 5, 5, 5);
this.bodyTextMorph = new philmaker.playground.StructuredContentTextMorph(rectangle, this.prepareSampleText(), 'body');
this.bodyTextMorph.setFontFamily('Optima');
this.bodyTextMorph.setFontSize(14);
this.bodyTextMorph.padding = Rectangle.inset(5, 5, 5, 5);
this.footerTextMorph = new philmaker.playground.StructuredContentTextMorph(rectangle, 'Copyright 2009, Bullwinkle J. Moose', 'footer');
this.footerTextMorph.setFontSize(10);
this.footerTextMorph.setFontFamily('Optima');
this.footerTextMorph.padding = Rectangle.inset(5, 5, 5, 5);
this.addMorph(this.headerTextMorph, {
weight : 0.2,
margin : new philmaker.foundation.Insets(0, 0, 5, 0)
});
this.addMorph(this.bodyTextMorph, {
weight : 0.2,
margin : new philmaker.foundation.Insets(5, 0, 5, 0)
});
this.addMorph(this.footerTextMorph, {
weight : 0.2,
margin : new philmaker.foundation.Insets(5, 0, 0, 0)
});
if (philmaker.foundation.layoutBorders) {
this.setBorderWidth(1);
this.setBorderColor(Color.red);
}
this.adjustForNewBounds(); // hack for now
//WorldMorph.current().scheduleForLater(new SchedulableAction(this, 'invokeCentering'), 5, false);
},
invokeCentering : function() { // bug: if I center the text and size narrow columns: exceptions are thrown and a column breaks out
this.headerTextMorph.setAlignment('center');
this.footerTextMorph.setAlignment('center');
},
prepareSampleText : function() {
var result = '';
for (var i = 0; i < 2; i++) {
result = result + philmaker.foundation.sampleText + '\n\n';
}
return result;
}
});
philmaker.foundation.TextMorph.subclass('philmaker.playground.StructuredContentTextMorph', {
suppressHandles : true,
openForDragAndDrop : false,
styleClass : [],
initialize : function($super, rectangle, text, name) {
$super(rectangle, text);
this.myName = name;
this.setFillOpacity(0.0);
this.setBorderWidth(1);
this.setBorderColor(Color.hsb(0.0, 0.0, 0.95));
this.setTextColor(Color.hsb(0.0, 0.0, 0.4));
this.focusHaloBorderWidth = 0;
this.padding = Rectangle.inset(0, 0, 0, 0);
if (philmaker.foundation.layoutBorders) {
this.setBorderWidth(1);
this.setBorderColor(Color.red);
}
}
});
Morph.subclass('philmaker.playground.Shelf', { // using scaling for workspaces
// until this is written, we just use the selection tray scaling
initialize: function($super) { // workspaces are stacked horizontally or vertically and have scrolling buttons
// look into FishEye too
$super(new lively.scene.Rectangle(new Rectangle(0, 0, 100, 100)));
this.setPosition(pt(100, 100));
},
openForDragAndDrop : true
});
Morph.subclass('philmaker.playground.ShelfBox', { // fixme: should be resizale and possibly still scalable
// cool demo: animation runnning inside a toolbox
openForDragAndDrop : false, // omg what to do if put one box inside another?
suppressHandles : true,
initialize: function($super) {
$super(new lively.scene.Rectangle(new Rectangle(100, 100, 100, 100)));
if (false) philmaker.foundation.layoutBorders = true;
this.suppressHandles = true;
this.setFill(Color.white);
this.setFillOpacity(1.0);
this.setBorderColor(Color.hsb(0, 0.0, 0.9));
this.setBorderWidth(6);
this.shapeRoundEdgesBy(10);
var textMorph = new philmaker.foundation.TextMorph(Rectangle(0, 0, 100, 100), 'Toolbox');
textMorph.setFillOpacity(0.0);
textMorph.setTextColor(Color.hsb(0, 0.0, 0.90));
textMorph.beLabel();
textMorph.setFontFamily('Marker Felt');
textMorph.setFontSize(20);
textMorph.setPosition(pt(8, 34));
this.addMorph(textMorph);
var container = new philmaker.playground.ShelfBoxContainer();
this.addMorph(container);
},
addCover : function() {
var cover = new philmaker.playground.ShelfBoxCover(this.shape.bounds());
this.addMorph(cover);
},
getContainer : function() {
return philmaker.foundation.Utility.getSubmorphByClass(this, philmaker.playground.ShelfBoxContainer);
},
getCover : function() {
return philmaker.foundation.Utility.getSubmorphByClass(this, philmaker.playground.ShelfBoxCover);
},
onMouseUp : function(event) { // ShelfBoxContainer.onMouseDown enables this
event.hand.setMouseFocus(null);
}
});
Morph.subclass('philmaker.playground.ShelfBoxContainer', {
openForDragAndDrop : true,
suppressHandles : true,
handlesMouseDown : Functions.True,
initialize: function($super) {
$super(new lively.scene.Rectangle(new Rectangle(10, 10, 80, 80)));
this.suppressHandles = true;
this.setFillOpacity(0.0);
this.setBorderWidth(0);
if (philmaker.foundation.layoutBorders) {
this.setBorderWidth(1);
this.setBorderColor(Color.red);
}
this.restoreBounds();
},
initializeTransientState : function() {
this.openForDragAndDrop = true;
},
restoreBounds : function() {
this.restorePosition();
this.setExtent(pt(80, 80));
},
restorePosition : function() {
this.setPosition(pt(10, 10));
},
addMorph : function($super, morph) { // issue: I am not seeing that Lively will notify me of a drop action, it only calls addMorph on the receiver
// as a result I hack this to be able know the extent of the entire set of dropped morphs first
if (! (morph.owner instanceof HandMorph)) { // so know how much to scale to fit the size of the container
$super(morph);
} else {
if ((! this.union) && (this.hasSubmorphs())) { // fixme: need a real flag instead of using this.union as the in process flag
return;
}
this.setPosition(pt(0, 0));
var handMorph = morph.owner;
var dropDelta = handMorph.getPosition().subPt(this.worldPoint(this.getPosition()));
if (! this.union) {
this.union = new Rectangle(0, 0, 0, 0);
var self = this;
handMorph.carriedMorphsDo(function(m) {
var rect = new Rectangle(m.getPosition().x, m.getPosition().y, m.getExtent().x, m.getExtent().y);
self.union = self.union.union(rect);
});
handMorph.carriedMorphsDo(function(m) {
m.translateBy(pt(-self.union.x, -self.union.y));
m.translateBy(pt(-dropDelta.x, -dropDelta.y));
});
var max = Math.max(this.union.width, this.union.height);
var offset = this.getBorderWidth();
this.scaleReduction = (this.getExtent().y / max);
this.setExtent(pt(this.union.width, this.union.height));
}
var result = $super(morph);
var carryCount = 0;
handMorph.carriedMorphsDo(function(m) {
carryCount++;
});
if (carryCount == 0) {
this.restorePosition();
this.union = null;
this.setScale(this.scaleReduction); // fixme: must take into account existing scaling of dropped morphs
var shelfBox = this.owner;
shelfBox.addCover();
}
}
},
onMouseDown : function(event) {
if (this.mouseFocus != this.owner) {
event.hand.setMouseFocus(this.owner);
}
},
addAllDroppedMorphs : function(morphs) {
},
/*
pickMeUp : function(event) {
this.owner.pickMeUp(event);
},
dragMe : function(event) {
this.owner.dragMe(event);
},
*/
remove : function() {
this.owner.remove();
}
});
Morph.subclass('philmaker.playground.ShelfBoxCover', {
openForDragAndDrop : false,
suppressHandles : true,
initialize: function($super, rectangle) {
$super(new lively.scene.Rectangle(rectangle));
this.suppressHandles = true;
this.setFillOpacity(0.0);
this.setBorderWidth(0);
if (philmaker.foundation.layoutBorders) {
this.setBorderWidth(1);
this.setBorderColor(Color.green);
}
this.handlesMouseDown = Functions.True;
},
initializeTransientState : function() {
this.openForDragAndDrop = false;
this.handlesMouseDown = Functions.True;
},
onMouseDown : function(event) {
this.beginGrab(event);
},
onMouseUp : function(event) {
event.hand.dropMorphsOn(this.world()); // or locate actual receiver?
this.owner.removeMorph(this);
},
beginGrab : function(event) { // reference material: PasteUpMorph onMouseDown & makeSelection
var shelfBox = this.owner;
var container = shelfBox.getContainer();
if (container.hasSubmorphs()) {
container.setScale(1.0);
var rectangle = event.point().asRectangle();
rectangle.width = container.getExtent().x;
rectangle.height = container.getExtent().y;
rectangle = rectangle.expandBy(5);
var grabMorph = null;
var dragDelta = event.hand.getPosition().subPt(this.worldPoint(container.getPosition()));
var world = this.world();
if (container.submorphs.length == 1) {
grabMorph = container.submorphs[0];
grabMorph.translateBy(pt(dragDelta.x, dragDelta.y));
world.addMorph(grabMorph);
} else {
var selectionMorph = new SelectionMorph(rectangle);
container.submorphs.clone().reverse().forEach(function(submorph) {
submorph.translateBy(pt(dragDelta.x, dragDelta.y));
world.addMorph(submorph);
selectionMorph.selectedMorphs.push(submorph);
});
grabMorph = selectionMorph;
this.world().currentSelection = grabMorph;
}
container.restoreBounds();
this.world().addMorph(grabMorph);
event.hand.grabMorph(grabMorph, event);
}
},
pickMeUp : function(event) {
this.owner.pickMeUp(event);
},
dragMe : function(event) {
this.owner.dragMe(event);
},
remove : function() {
this.owner.remove();
}
});
Morph.subclass('philmaker.playground.TagMorph', { // feedback: would be nice if I could pass points here
// but lively.scene may not depend on Base
openForDragAndDrop : false,
suppressHandles : true,
reshape : Functions.Null,
tagWidth : 80,
tagHeight : 20,
insetX : 6,
insetY : 6,
initialize : function($super) {
var shape = this.createShape();
$super(shape);
this.setFill(Color.hsb(217, 0.66, 0.93));
this.setBorderColor(Color.hsb(217, 0.66, 0.85));
this.setBorderWidth(1);
var rectangle = new Rectangle(0, 0, this.tagWidth, this.tagHeight);
var textMorph = new philmaker.playground.TagTextMorph(rectangle);
this.addMorph(textMorph);
textMorph.setPosition(pt(this.insetX + 6, 2));
if (true) {
this.rotateBy(-0.1);
}
},
createShape : function() {
var definition = this.createShapeDefinition();
if (false) {
return new lively.scene.Path(definition);
} else {
return new lively.scene.Polygon(definition);
}
},
createShapeDefinition : function() {
if (false) {
var p = pt(0, 0);
var elements = [];
elements.push(new lively.scene.MoveTo(p.x, p.y = p.y + this.insetY));
elements.push(new lively.scene.LineTo(p.x = p.x + this.insetX, p.y = p.y - this.insetY));
elements.push(new lively.scene.LineTo(p.x = (this.tagWidth - this.insetX), p.y));
elements.push(new lively.scene.LineTo(p.x, p.y = p.y + this.tagHeight));
elements.push(new lively.scene.LineTo(p.x = p.x - (this.tagWidth - (this.insetX * 2)), p.y));
elements.push(new lively.scene.LineTo(p.x = p.x - this.insetX, p.y = p.y - this.insetY));
elements.push(new lively.scene.LineTo(p.x, p.y = p.y - (this.tagHeight - (this.insetY * 2))));
//elements.push(new lively.scene.ClosePath()); // causes an exception when rendering
return elements;
} else {
var p = pt(0, 0);
var points = [];
points.push(p = p.copy().addXY(0, this.insetY));
points.push(p = p.copy().addXY(this.insetX, - this.insetY));
points.push(p = p.copy().addXY(this.tagWidth - (this.insetX * 2), 0));
points.push(p = p.copy().addXY(0, this.tagHeight));
points.push(p = p.copy().addXY(-(this.tagWidth - (this.insetX * 2)), 0));
points.push(p = p.copy().addXY(-this.insetX, -this.insetY));
points.push(p = p.copy().addXY(0, - (this.tagHeight - (this.insetY * 2))));
return points;
}
},
updateExtent : function() {
var textMorph = philmaker.foundation.Utility.getSubmorphByClass(this, TextMorph);
var length = textMorph.textString.length;
var charBounds = textMorph.getCharBounds(length - 1);
var width = charBounds.x + charBounds.width;
var extent = textMorph.getExtent();
extent.y = width;
textMorph.setExtent(extent);
this.tagWidth = width + 26;
var definition = this.createShapeDefinition();
if (false) {
this.shape.setElements(definition);
} else {
this.shape.setVertices(definition);
}
},
setValue : function(value) {
var textMorph = philmaker.foundation.Utility.getSubmorphByClass(this, TextMorph);
return textMorph.setTextString(value);
},
getValue : function() {
var textMorph = philmaker.foundation.Utility.getSubmorphByClass(this, TextMorph);
return textMorph.textString;
}
});
TextMorph.subclass('philmaker.playground.TagTextMorph', { // fixme: selection x extent does not work very well after resizing
openForDragAndDrop : false,
suppressHandles : true,
reshape : Functions.Null,
initialize : function($super, rectangle) {
$super(rectangle);
this.setFillOpacity(0);
this.setBorderWidth(0);
this.setFontFamily('Marker Felt');
this.setFontSize(16);
this.setTextColor(Color.white);
this.focusHaloBorderWidth = 0;
this.padding = new Rectangle(0, 0, 0, 0);
this.setTextString('Untitled');
this.setWrapStyle(lively.Text.WrapStyle.None);
if (false) {
this.setBorderWidth(1);
this.setBorderColor(Color.red);
this.setTextColor(Color.red);
}
},
setTextString : function($super, textString) {
$super(textString);
if (this.owner) {
this.owner.updateExtent();
}
},
onKeyDown : function($super, event) {
$super(event);
var owner = this.owner;
if (owner && (owner instanceof philmaker.playground.TagMorph)) {
var tagMorph = owner;
console.log('calling updateExtent');
tagMorph.updateExtent();
}
}
});
BoxMorph.subclass('philmaker.playground.RecordMorph', { // may need a way to expand and collapse records (triangle at right edge?)
// for the demo: use toolbox to house some runnable code notes
initialize : function($super, spec) {
var rectangle = new Rectangle(100, 100, 700, 34);
$super(rectangle);
this.setFill(Color.white);
this.setBorderColor(Color.hsb(217, .5, 1));
this.setBorderWidth(2);
this.shapeRoundEdgesBy(10);
if (! spec) {
spec = {
kind : 'Task',
title : 'Wash the Dog',
rating : 10,
tags : ['Chores', 'Complete']
};
}
var padding = 10;
var x = 10;
var morph;
morph = new philmaker.playground.TagMorph();
morph.setValue(spec.kind);
morph.setPosition(pt(x, 10));
this.addMorph(morph);
x = x + morph.getExtent().scaleByPt(morph.scalePoint).x + padding;
morph = new philmaker.playground.RecordTextMorph(new Rectangle(0, 0, 200, 20), spec.title, 'title');
morph.setPosition(pt(x, 9));
this.addMorph(morph);
x = x + morph.getExtent().scaleByPt(morph.scalePoint).x + padding;
var titleMorph = morph;
morph = new philmaker.playground.RatingMorph();
morph.name = 'rating';
morph.setFill(Color.hsb(217, .66, .93));
morph.setLitColor(Color.white);
morph.setUnlitColor(Color.hsb(217, 0.66, 0.93));
morph.setValue(spec.rating);
morph.setScale(0.8);
morph.setPosition(pt(x, 7));
this.addMorph(morph);
x = x + morph.getExtent().scaleByPt(morph.scalePoint).x + padding;
for (var i = 0; i < spec.tags.length; i++) {
var tagText = spec.tags[i];
morph = new philmaker.playground.TagMorph();
morph.setValue(tagText);
morph.setPosition(pt(x, 10));
this.addMorph(morph);
x = x + morph.getExtent().scaleByPt(morph.scalePoint).x + padding;
}
var y = this.getExtent().y;
if (spec.fields) {
var x2 = titleMorph.getPosition().x;
var y = titleMorph.getPosition().y + titleMorph.getExtent().scaleByPt(titleMorph.scalePoint).y + 0;
for (var i = 0; i < spec.fields.length; i++) {
var field = spec.fields[i];
morph = new philmaker.playground.RecordTextMorph(new Rectangle(0, 0, 200, 20), field.value, field.name);
morph.setPosition(pt(x2, y));
this.addMorph(morph);
y = y + morph.getExtent().scaleByPt(morph.scalePoint).y + 0;
}
}
this.setPosition(pt(100, 100));
var extent = this.getExtent();
extent.x = x + 4;
extent.y = y;
this.setExtent(extent);
}
});
philmaker.foundation.TextMorph.subclass('philmaker.playground.RecordTextMorph', {
initialize : function($super, rectangle, value, name) {
$super(rectangle, value);
this.setFillOpacity(0);
this.setBorderWidth(0);
this.setFontFamily('Marker Felt');
this.setFontSize(16);
this.setTextColor(Color.hsb(217, .66, .93));
this.focusHaloBorderWidth = 0;
this.padding = new Rectangle(0, 0, 0, 0);
this.setWrapStyle(lively.Text.WrapStyle.None);
this.name = name;
}
});
Object.subclass('philmaker.playground.RecordAssistant', {
initialize : function() {
}
});
philmaker.playground.RecordAssistant.findAllRecords = function() { // use Namespace.addMethods ???
var records = [];
WorldMorph.current().withAllSubmorphsDo(function() {
if (philmaker.playground.RecordAssistant.isRecord(this)) {
records.push(this);
}
});
return records;
};
philmaker.playground.RecordAssistant.findRecordsTagged = function(records, text) {
var results = [];
records.forEach(function() {
var record = this;
var tags = philmaker.foundation.Utility.getSubmorphsByClass(this, philmaker.playground.TagMorph);
tags.forEach(function() {
var value = this.getValue();
if (value == text) {
results.push(record);
}
});
});
return results;
};
philmaker.playground.RecordAssistant.getFieldValue = function(record, field) {
var value = null;
record.withAllSubmorphsDo(function() {
if (this.field && this.field == field) {
if (this.textString) {
value = this.textString;
} else if (this instanceof philmaker.foundation.RatingMorph) {
value = submorph.getValue();
}
}
});
return value;
};
philmaker.playground.RecordAssistant.isRecord = function(morph) {
var isRecord = false;
if ((morph.isRecord) && (morph.isRecord == true)) {
isRecord = true;
} else if (morph instanceof philmaker.playground.RecordMorph) {
isRecord = true;
}
return isRecord;
};
philmaker.foundation.TextMorph.subclass('philmaker.playground.BadgeMorph', {
initialize : function($super, text) {
$super(new Rectangle(0, 0, 200, 40), text);
this.setFillOpacity(1.0);
this.setBorderWidth(1.0);
this.setFontFamily('Optima');
this.setFontSize(18);
this.setTextColor(Color.hsb(0, 0.0, 0.15));
this.focusHaloBorderWidth = 0;
this.padding = new Rectangle(8, 8, 8, 8);
this.setWrapStyle(lively.Text.WrapStyle.None);
}
});
philmaker.foundation.ImageMorph.subclass('philmaker.playground.ImageMorph', { // todo: change the name and implementation of this to CaptionedImage
// image, title/caption, borders, (maybe photodate if the morph doesn't get too busy) (later even: rating)
initialize : function($super) { // look into ImageButtonMorph
var rectangle = new Rectangle(100, 100, 300, 250);
var imagePath = 'http://farm4.static.flickr.com/3228/2601103409_9b2817c6bf_o.png';
$super(rectangle, imagePath);
this.setOpacity(0.85);
}
});
philmaker.foundation.BoxLayoutMorph.subclass('philmaker.playground.ScrollableStickyNote', { // need to be able to detect system fonts or the system
// and also offer alternatives
initialize : function($super) {
$super(new Rectangle(200, 200, 300, 300));
this.setOrientation('vertical');
this.setFill(Color.hsb(59, .36, 1.0));
this.setFillOpacity(1.0);
this.setBorderColor(Color.rgb(255, 240, 146));
this.setBorderWidth(1);
this.setStrokeOpacity(1.0);
this.rotateBy(-0.05);
this.suppressHandles = false;
var textMorph = new philmaker.foundation.TextMorph(new Rectangle(0, 0, 300, 300), this.prepareSampleText());
textMorph.setTextColor(Color.hsb(29, 1.0, 0.5));
textMorph.setFontFamily('Marker Felt');
textMorph.setFontSize(18);
textMorph.setFill(Color.rgb(255, 254, 164));
textMorph.setFillOpacity(0.0);
textMorph.focusHaloBorderWidth = 0;
var scrollPane = new philmaker.playground.NoteScrollPane(textMorph, new Rectangle(0, 0, 300, 300));
this.addMorph(scrollPane, {
weight : 1.0,
margin : new philmaker.foundation.Insets(20, 1, 1, 1)
});
this.adjustForNewBounds(); // hack for now
},
prepareSampleText : function() {
var result = '';
for (var i = 0; i < 4; i++) {
result = result + philmaker.foundation.sampleText + '\n\n';
}
return result;
}
});
philmaker.foundation.BoxLayoutMorph.subclass('philmaker.playground.RunnableCodeNote', {
initialize : function($super) { // need to study TitleBarMorph to figure out how to make title bar draggable yet attached
$super(new Rectangle(200, 200, 600, 150));
this.setOrientation('vertical');
this.setFill(Color.hsb(0.0, 0.0, 0.98));
this.setFillOpacity(1.0);
this.setBorderColor(Color.hsb(0.0, 0.0, 0.93));
this.setBorderWidth(1);
this.setStrokeOpacity(1.0);
this.suppressHandles = false;
this.textMorph = new philmaker.foundation.TextMorph(new Rectangle(0, 0, 300, 300), this.prepareSampleText());
this.textMorph.setTextColor(Color.hsb(0.0, 0.0, 0.0));
if (true) {
this.textMorph.setFontFamily('Monaco');
this.textMorph.setFontSize(10);
} else {
this.textMorph.setFontFamily('Verdana');
this.textMorph.setFontSize(10);
this.textMorph.emphasizeBoldItalic({
style : 'bold'
});
}
this.textMorph.setFill(Color.hsb(0.0, 0.0, 0.98)); // fixme: investigate why this is necessary
this.textMorph.setFillOpacity(0.0); // would like for setFillOpacity(0.0) to just work
this.textMorph.focusHaloBorderWidth = 0;
this.textMorph.openForDragAndDrop = false;
var scrollPane = new philmaker.playground.NoteScrollPane(this.textMorph, new Rectangle(0, 0, 300, 300));
var controls = new philmaker.playground.RunnableCodeNoteControlMorph(this);
this.addMorph(scrollPane, {
weight : 1.0,
margin : new philmaker.foundation.Insets(20, 1, 1, 1)
});
this.addMorph(controls, {
weight : 26,
margin : new philmaker.foundation.Insets(3, 2, 0, 0)
});
this.adjustForNewBounds(); // hack for now
},
evaluate : function() {
var text = this.textMorph.textString;
try {
eval(text);
} catch (e) {
console.log('RunnableCodeNote evaluation failed.');
console.log(e);
}
},
prepareSampleText : function() {
var result = '';
for (var i = 0; i < 3; i++) {
result = result + 'console.log(\'Let\'s all go to the lobby...\');' + '\n';
}
return result;
}
});
philmaker.foundation.BoxLayoutMorph.subclass('philmaker.playground.RunnableCodeNoteControlMorph', {
initialize : function($super, codeNote) {
$super(new Rectangle(0, 0, 0, 0));
this.codeNote = codeNote;
this.setOrientation('horizontal');
this.setFillOpacity(0.0);
this.setStrokeOpacity(1.0);
var spacerMorph = new philmaker.foundation.BasicMorph(new lively.scene.Rectangle(new Rectangle(0, 0, 0, 0)));
var buttonMorph = new philmaker.playground.RunnableCodeNoteEvaluateButton(new Rectangle(0, 0, 300, 300), 'Evaluate', this.codeNote);
this.addMorph(buttonMorph, {
weight : 80, // this is an issue with my BoxLayoutMorph, if not wide enough then clips mouse event region
margin : new philmaker.foundation.Insets(0, 2, 0, 0)
});
this.addMorph(spacerMorph, {
weight : 1.0,
margin : new philmaker.foundation.Insets(0, 0, 0, 0)
});
this.adjustForNewBounds(); // hack for now
}
});
philmaker.foundation.TextMorph.subclass('philmaker.playground.RunnableCodeNoteEvaluateButton', { // fixme: slow down and try to use ButtonMorph instead
handlesMouseDown : Functions.True,
initialize : function($super, rectangle, text, codeNote) {
$super(rectangle, text);
this.codeNote = codeNote;
this.openForDragAndDrop = false;
this.suppressHandles = true;
this.initializeFills();
this.focusHaloBorderWidth = 0;
this.openForDragAndDrop = false;
this.beLabel();
this.applyStyle({ // without this after beLabel, setBorderColor below will not work
borderWidth : 1,
fontSize : 12,
});
if (false) {
buttonMorph.setBold(true);
}
this.setTextColor(Color.hsb(0, 0.0, 0.4));
this.setFill(Color.hsb(0, 0.0, 0.85));
this.setFill(this.normalFill);
this.setBorderColor(Color.hsb(0, 0.0, 0.75));
this.setStrokeOpacity(1.0);
this.setFillOpacity(1.0);
this.shapeRoundEdgesBy(5);
this.padding = Rectangle.inset(8, 3, 8, 3); // left, top, right, bottom (order?)
this.handlesMouseDown = Functions.True;
},
initializeTransientState : function() {
this.initializeFills();
},
initializeFills : function() {
var hue = 0;
var saturation = 0.0;
this.normalFill = new lively.paint.LinearGradient([
new lively.paint.Stop(0, Color.hsb(hue, saturation, 0.99)),
new lively.paint.Stop(1, Color.hsb(hue, saturation, 0.70))
], lively.paint.LinearGradient.NorthSouth);
this.pressedFill = new lively.paint.LinearGradient([
new lively.paint.Stop(0, Color.hsb(hue, saturation, 0.79)),
new lively.paint.Stop(1, Color.hsb(hue, saturation, 0.50))
], lively.paint.LinearGradient.NorthSouth);
},
ignoreEvents : function($super) { // because beLabel calls ignore events
;
},
onMouseDown : function($super, evt) {
this.setFill(this.pressedFill);
return true;
},
onMouseUp : function($super, evt) {
this.setFill(this.normalFill);
this.codeNote.evaluate(); // fixme: need to look into Lively's model mechanism
return true;
}
});
ScrollPane.subclass('philmaker.playground.NoteScrollPane', {
initialize : function($super, morphToClip, initialBounds) {
$super(morphToClip, initialBounds);
this.scrollBar.takesKeyboardFocus = Functions.False;
this.scrollBar.applyStyle({
borderWidth : 0,
borderColor : Color.red
});
this.scrollBar.setFillOpacity(0.0);
this.scrollBar.setBorderWidth(0); // this function is all a quick fix because Slider.adjustSliderParts
this.scrollBar.setBorderWidth = function($super, width) { // invokes this: this.setBorderWidth(this.slider.getBorderWidth());
$super(0);
};
this.scrollBar.slider.setFill(Color.hsb(0, 0.0, 0.0));
this.scrollBar.slider.setFillOpacity(0.03);
this.scrollBar.slider.setBorderWidth(1);
this.scrollBar.slider.setBorderColor(Color.hsb(29, 0.0, 0.0));
this.scrollBar.slider.setStrokeOpacity(0.06);
this.scrollBarWidth = 8;
if (true) {
this.scrollBar.slider.setFillOpacity(0.05);
this.scrollBar.slider.setStrokeOpacity(0.065);
}
}
});
philmaker.foundation.TextMorph.subclass('philmaker.playground.StickyNote', { // need to be able to detect system fonts or the system
// and also be able to plug in alternates
initialize : function($super) {
var rectangle = new Rectangle(200, 200, 300, 10);
$super(rectangle);
this.setFill(Color.rgb(255, 254, 164));
this.setBorderColor(Color.rgb(255, 240, 146));
this.setBorderWidth(1);
this.setFillOpacity(1.0);
this.setStrokeOpacity(1.0);
this.setTextColor(Color.rgb(127, 62, 0));
this.setFontFamily('Marker Felt');
this.setFontSize(18);
this.focusHaloBorderWidth = 0;
this.rotateBy(-0.05);
this.textString = philmaker.foundation.sampleText;
}
});
philmaker.foundation.TextMorph.subclass('philmaker.playground.CodeNote', { // need to be able to detect system fonts, or the users system
// and also offer font alternatives
initialize : function($super) { // add: run, preamble, postscript
var rectangle = new Rectangle(220, 220, 300, 10);
$super(rectangle);
this.setFill(Color.hsb(0, 0.0, 0.9));
this.setBorderColor(Color.hsb(0, 0.0, 0.8));
this.setBorderWidth(1);
this.setFillOpacity(1.0);
this.setStrokeOpacity(1.0);
this.setTextColor(Color.hsb(0, 0.0, 0.0));
if (true) {
this.setFontFamily('Monaco');
this.setFontSize(10);
this.emphasizeBoldItalic({
style : ''
});
} else {
this.setFontFamily('Verdana');
this.setFontSize(10);
this.emphasizeBoldItalic({
style : 'bold'
});
}
this.focusHaloBorderWidth = 0;
var text = '';
for (var i = 0; i < 3; i++) {
text = text + 'console.log(\'Let\'s all go to the lobby...\');' + '\n';
}
this.setTextString(text);
}
});
Morph.subclass('philmaker.playground.WormholeMorph', {
openForDragAndDrop: false,
suppressHandles : true,
styleClass: [],
initialize : function($super) {
$super(new lively.scene.Ellipse(pt(200, 200), 55));
this.setFillOpacity(1.0);
//this.setFill(Color.hsb(209, .9, .99));
this.setFill(Color.hsb(287, .9, .99));
this.setBorderWidth(0);
for (var i = 0; i < 8; i++) {
var leg = new philmaker.playground.WormholeLegMorph();
leg.setPosition(pt(-50, 0));
leg.rotateBy(0.78 * i);
this.addMorph(leg);
}
this.setScale(.4);
if (false) {
this.beClipMorph();
}
WorldMorph.current().scheduleForLater(new SchedulableAction(this, 'invokeSteppingScript'), 5, false);
},
invokeSteppingScript : function() {
this.startStepping(50, 'twirl');
},
twirl : function() {
this.rotateBy(0.015);
}
});
Morph.subclass('philmaker.playground.WormholeLegMorph', {
openForDragAndDrop : false,
suppressHandles : true,
initialize : function($super) {
var path = [];
path.push(new lively.scene.MoveTo(0, 0));
path.push(new lively.scene.QuadCurveTo(-50, 50, -35, 10));
path.push(new lively.scene.LineTo(-25, 50));
path.push(new lively.scene.QuadCurveTo(0, 0, -20, 10));
path.push(new lively.scene.ClosePath());
$super(new lively.scene.Path(path));
//this.setPosition(pt(100, 100));
//this.shape.translateBy(200, 200);
//this.moveOriginBy(pt(50, 50));
//this.setScalePoint(pt(3, 3));
//this.setFill(Color.hsb(209, .39, .99));
this.setFill(Color.hsb(287, .39, .99));
this.setBorderWidth(0);
}
});
philmaker.foundation.BoxLayoutMorph.subclass('philmaker.playground.BoxLayoutMorph', { // philmaker.foundation.BoxLayoutMorph test morph
margin : new Rectangle(8, 8, 8, 8),
initialize : function($super) {
$super(new Rectangle(100, 100, 300, 300));
this.setOrientation('horizontal');
this.setFill(Color.white);
this.setFillOpacity(1.0);
this.setBorderColor(Color.red);
this.setBorderWidth(1);
this.shapeRoundEdgesBy(0);
this.padding = Rectangle.inset(10, 10, 10, 10);
this.suppressHandles = false;
var rectangle = new Rectangle(0, 0, 150, 500);
this.leftTextMorph = new TextMorph(rectangle, this.prepareSidebarText(), 'left');
this.leftTextMorph.padding = Rectangle.inset(5, 5, 5, 5);
this.centerTextMorph = new philmaker.playground.BoxLayoutMorph2();
this.centerTextMorph.padding = Rectangle.inset(5, 5, 5, 5);
this.rightTextMorph = new TextMorph(rectangle, this.prepareSidebarText(), 'right');
this.rightTextMorph.padding = Rectangle.inset(5, 5, 5, 5);
this.addMorph(this.leftTextMorph, {
weight : 0.2,
margin : new philmaker.foundation.Insets(5, 5, 5, 5)
});
this.addMorph(this.centerTextMorph, {
weight : 0.6,
margin : new philmaker.foundation.Insets(5, 5, 5, 5)
});
this.addMorph(this.rightTextMorph, {
weight : 0.2,
margin : new philmaker.foundation.Insets(5, 5, 5, 5)
});
if (philmaker.foundation.layoutBorders) {
this.setBorderWidth(1);
this.setBorderColor(Color.red);
}
this.adjustForNewBounds(); // hack for now
},
prepareSidebarText : function() {
var result = '';
for (var i = 0; i < 20; i++) {
result = result + 'sidebar link' + '\n';
}
return result;
}
});
philmaker.foundation.BoxLayoutMorph.subclass('philmaker.playground.BoxLayoutMorph2', { // philmaker.foundation.BoxLayoutMorph test morph
initialize : function($super) {
$super(new Rectangle(0, 0, 100, 300));
this.setOrientation('vertical');
this.setFill(Color.white);
this.setFillOpacity(1.0);
this.setBorderWidth(1);
this.shapeRoundEdgesBy(0);
this.padding = Rectangle.inset(10, 10, 10, 10);
var rectangle = new Rectangle(0, 0, 150, 500);
this.headerTextMorph = new TextMorph(rectangle, 'Lively: The Web Done Right', 'header');
this.headerTextMorph.padding = Rectangle.inset(5, 5, 5, 5);
this.bodyTextMorph = new TextMorph(rectangle, this.prepareSidebarText(), 'body');
this.bodyTextMorph.padding = Rectangle.inset(5, 5, 5, 5);
this.footerTextMorph = new TextMorph(rectangle, 'Copyright Bullwikle J. Moose', 'footer');
this.footerTextMorph.padding = Rectangle.inset(5, 5, 5, 5);
this.addMorph(this.headerTextMorph, {
weight : 0.2,
margin : new philmaker.foundation.Insets(0, 5, 5, 5)
});
this.addMorph(this.bodyTextMorph, {
weight : 0.2,
margin : new philmaker.foundation.Insets(5, 5, 5, 5)
});
this.addMorph(this.footerTextMorph, {
weight : 0.2,
margin : new philmaker.foundation.Insets(5, 5, 0, 5)
});
if (philmaker.foundation.layoutBorders) {
this.setBorderWidth(1);
this.setBorderColor(Color.red);
}
this.adjustForNewBounds(); // hack for now
},
prepareSidebarText : function() {
var result = '';
for (var i = 0; i < 20; i++) {
result = result + 'sidebar link' + '\n';
}
return result;
}
});
PanelMorph.subclass('philmaker.playground.PanelMorph', { // was previously a simple isolated test case to study PanelMorph and padding
// underlying issue: PanelMorph does not appear to account for gaps between submorphs (review)
initialize : function($super) { // and GridLayoutMorph does not appear to support adjustForNewBounds
},
adjustForNewBounds : function ($super) {
$super();
var extent = this.getExtent();
var padding = this.padding;
this.submorphs.forEach(function(submorph) {
submorph.setPosition(pt(padding.left, padding.top));
submorph.setExtent(pt(extent.x - (padding.left + padding.right), extent.y - (padding.top + padding.bottom)));
});
}
});
Object.subclass('MetamorphosisDemo', {
initialize : function() {
console.log('Hello world');
var morph = Morph.makeRectangle(pt(100, 100), pt(60, 30));
morph.suppressHandles = true;
morph.openForDragAndDrop = false;
morph.handlesMouseDown = Functions.True;
var self = morph;
WorldMorph.current().addMorph(morph);
this.metamorphObject(morph, philmaker.playground.TickleMorph);
},
metamorphObject : function(object, clazz) {
if (true) {
object.__proto__ = clazz.prototype;
} else {
for (var i in clazz.prototype) {
if (clazz.prototype.hasOwnProperty(i)) {
object[i] = clazz.prototype[i];
}
}
}
}
});
Morph.subclass('philmaker.playground.TickleMorph', {
handlesMouseDown : Functions.True,
initialize : function($super, shape) {
$super(shape);
},
initializeTransientState : function() {
this.handlesMouseDown = Functions.True;
this.suppressHandles = true;
this.openForDragAndDrop = false;
},
onMouseDown : function(event) {
window.alert('Please do not tickle me.');
if (this instanceof philmaker.playground.TickleMorph) {
console.log('instanceof philmaker.playground.TickleMorph');
} else {
console.log('not instanceof philmaker.playground.TickleMorph');
}
}
});
try {
var worldInitializer = new philmaker.playground.WorldInitializer();
if (true) {
worldInitializer.initializePreamble();
} else {
worldInitializer.initializePostscript();
}
} catch (e) {
console.log('Exception in philmaker.playground.WorldInitializer: ' + JSON.serialize(e));
}
try {
new philmaker.playground.WorldInitializer();
} catch (e) {
console.log('Exception creating philmaker.playground.WorldInitializer');
console.log(e);
}
null
ServerPreparation:InstallingApache&Lively
18
22
false
null
8
null
null
Toolbox
20
null
null
null
PreparingApacheforLivelyThesestepswereperformedforLinuxunderUbuntu8.04HardyHeronrunningatslicehost.com.ConnectingtothemachineasrootusingsshfromMacOSX10.5.6Caveats:TheversionofApachewashavingtroublewithdigestauthenticationsothissetupexampleisusingbasicauthenticationonly.Therecommendationistousesomethingbetterthanbasicauthentication.Apachemodulesmaybeconfiguredpermanently(a2enmod)oratruntimeinhttpd.conf(LoadModule).WhenIrehearsedthesestepsforthiswriteup,IdidnotgetLoadModuleworkingreliablyquicklyforWebDavsoIstopped.RunningasrootIthinkisabadideabutIdiditforthiswriteup.#connect,updatethesystem,andinstalltheApachewebserverssh<username-or-root>@<host>sudoapt-getupdatesudoapt-getinstallapache2a2enmoddava2enmoddav_fsa2enmodauth_digesta2enmodproxya2enmodproxy_httpa2enmodrewriteapt-getinstallsubversioncd/varmkdirlively-kernelcdlively-kernelsvncheckouthttp://livelykernel.sunlabs.com/repository/lively-kernel/trunk/#copythekernelclientfilestowebdocumentroot-thiswillforceoverwritetheexistingApacheindex.htmlpage(warning!)cp-rftrunk/source/kernel/*/var/www#thesecommandsensurethatWebDavhaspermissionsforApache'sdocumentrootcd/varchmod-R755wwwchown-Rwww-datawwwchgrp-Rwww-datawww#importantcaveat:ifyoulatermanuallyuploadfiles,forexampleusingftpandnotWebDav,youlikelyneedtoissuetheseagain#createusernameandpasswordforWebDavaccesshtpasswd-c/etc/apache2/access<webdav-username>#otherwiseauthdigestwouldbelike:htdigest-c/etc/apache2/accessglobal<webdav-username>#editthewebserverconfigurationwithhttpd.confsource#ifyouarenotfamilarwiththevieditoritwilllikelybeeasierjusttouploadhttpd.confusinganftpclientvi/etc/apache2/httpd.conf#runorreloadthewebserversudo/etc/init.d/apache2restart#sudo/etc/init.d/apache2force-reload#nowconfiguretheLivelyinstall#pending
@\rsudo apt-get update\r\rsudo apt-get install apache2\ra2enmod dav\ra2enmod dav_fs\ra2enmod auth_digest\ra2enmod proxy\ra2enmod proxy_http\ra2enmod rewrite\r\rapt-get install subversion\rcd /var\rmkdir lively-kernel\rcd lively-kernel\rsvn checkout http://livelykernel.sunlabs.com/repository/lively-kernel/trunk/\r# copy the kernel client files to web document root - this will force overwrite the existing Apache index.html page (warning!)\rcp -rf trunk/source/kernel/* /var/www\r\r# these commands ensure that WebDav has permissions for Apache's document root\rcd /var\rchmod -R 755 www\rchown -R www-data www\rchgrp -R www-data www\r# important caveat: if you later manually upload files, for example using ftp and not WebDav, you likely need to issue these again\r\r# create username and password for WebDav access\rhtpasswd -c /etc/apache2/access \r# otherwise auth digest would be like: htdigest -c /etc/apache2/access global \r\r# edit the web server configuration with httpd.conf source\r# if you are not familar with the vi editor it will likely be easier just to upload httpd.conf using an ftp client\rvi /etc/apache2/httpd.conf\r\r# run or reload the web server\rsudo /etc/init.d/apache2 restart\r# sudo /etc/init.d/apache2 force-reload\r\r# now configure the Lively install\r# pending"]]>
null
0
true
596
true
595
@\rsudo apt-get update\r\rsudo apt-get install apache2\ra2enmod dav\ra2enmod dav_fs\ra2enmod auth_digest\ra2enmod proxy\ra2enmod proxy_http\ra2enmod rewrite\r\rapt-get install subversion\rcd /var\rmkdir lively-kernel\rcd lively-kernel\rsvn checkout http://livelykernel.sunlabs.com/repository/lively-kernel/trunk/\r# copy the kernel client files to web document root - this will force overwrite the existing Apache index.html page (warning!)\rcp -rf trunk/source/kernel/* /var/www\r\r# these commands ensure that WebDav has permissions for Apache's document root\rcd /var\rchmod -R 755 www\rchown -R www-data www\rchgrp -R www-data www\r# important caveat: if you later manually upload files, for example using ftp and not WebDav, you likely need to issue these again\r\r# create username and password for WebDav access\rhtpasswd -c /etc/apache2/access \r# otherwise auth digest would be like: htdigest -c /etc/apache2/access global \r\r# edit the web server configuration with httpd.conf source\r# if you are not familar with the vi editor it will likely be easier just to upload httpd.conf using an ftp client\rvi /etc/apache2/httpd.conf\r\r# run or reload the web server\rsudo /etc/init.d/apache2 restart\r# sudo /etc/init.d/apache2 force-reload\r\r# now configure the Lively install\r# pending"]]>
null
9
null
true
null
null
1
true
null
true
8
false
null
0.09650180940892641
null
Toolbox
20
null
null
null
<VirtualHost*>ServerAdminyou@yourdomain.comServerNameyourdomain.comServerAliaswww.yourdomain.comDocumentRoot/var/www<Directory/var/www>OptionsIndexesMultiViewsAllowOverrideNoneOrderallow,denyallowfromall</Directory><Location/>DAVOnAuthTypeBasicAuthName"RestrictedFiles"AuthUserFile/etc/apache2/access<LimitExceptGETOPTIONSPROPFIND>Requirevalid-user</LimitExcept></Location><Proxy*>Orderdeny,allowAllowfromall</Proxy>#seealso:trunk/source/kernel/example.htaccess&example.htpasswd#considertheimplicationsofalloftheseproxiesbeforehostingapublicsiteRewriteEngineOnRewriteRule^/proxy/news.com.com(.*)$http://news.com.com$1[P]RewriteRule^/proxy/news.cnet.com(.*)$http://news.cnet.com$1[P]RewriteRule^/proxy/weatherforecastmap.com(.*)$http://weatherforecastmap.com$1[P]RewriteRule^/proxy/feeds.bbc.co.uk(.*)$http://feeds.bbc.co.uk$1[P]RewriteRule^/proxy/finance.google.com(.*)$http://finance.google.com$1[P]RewriteRule^/proxy/download.finance.yahoo.com(.*)$http://download.finance.yahoo.com$1[P]RewriteRule^/proxy/feeds.feedburner.com(.*)$http://feeds.feedburner.com$1[P]RewriteRule^/proxy/blogs.sun.com(.*)$http://blogs.sun.com$1[P]RewriteRule^/proxy/feeds.tuaw.com(.*)$http://feeds.tuaw.com$1[P]RewriteRule^/proxy/api.flickr.com/services/rest/http://api.flickr.com/services/rest/?api_key=YOUR_KEY[QSA,P]</VirtualHost>
\n\tServerAdmin you@yourdomain.com\n\tServerName yourdomain.com\n\tServerAlias www.yourdomain.com\n\t\n\tDocumentRoot /var/www\n\t\n\t\tOptions Indexes MultiViews\n\t\tAllowOverride None\n\t\tOrder allow,deny\n\t\tallow from all\n\t\n\t\n\t\n\t\tDAV On\n\t\tAuthType Basic\n\t\tAuthName \"Restricted Files\"\n\t\tAuthUserFile /etc/apache2/access\n\t\t\n\t\t\tRequire valid-user\n\t\t\n\t\n\t\n\t\n\t\tOrder deny,allow\n\t\tAllow from all\n\t \n\n\t# see also: trunk/source/kernel/example.htaccess & example.htpasswd\r\t# consider the implications of all of these proxies before hosting a public site\n\tRewriteEngine On\r\tRewriteRule ^/proxy/news.com.com(.*)$ http://news.com.com$1 [P]\r\tRewriteRule ^/proxy/news.cnet.com(.*)$ http://news.cnet.com$1 [P]\r\tRewriteRule ^/proxy/weatherforecastmap.com(.*)$ http://weatherforecastmap.com$1 [P]\r\tRewriteRule ^/proxy/feeds.bbc.co.uk(.*)$ http://feeds.bbc.co.uk$1 [P]\r\tRewriteRule ^/proxy/finance.google.com(.*)$ http://finance.google.com$1 [P]\r\tRewriteRule ^/proxy/download.finance.yahoo.com(.*)$ http://download.finance.yahoo.com$1 [P]\r\tRewriteRule ^/proxy/feeds.feedburner.com(.*)$ http://feeds.feedburner.com$1 [P]\r\tRewriteRule ^/proxy/blogs.sun.com(.*)$ http://blogs.sun.com$1 [P]\r\tRewriteRule ^/proxy/feeds.tuaw.com(.*)$ http://feeds.tuaw.com$1 [P]\r\tRewriteRule ^/proxy/api.flickr.com/services/rest/ http://api.flickr.com/services/rest/?api_key=YOUR_KEY [QSA,P]\r\n"]]>
41
null
0
true
441
true
\n\t\r\t# see also"]]>
523
\n\tServerAdmin you@yourdomain.com\n\tServerName yourdomain.com\n\tServerAlias www.yourdomain.com\n\t\n\tDocumentRoot /var/www\n\t\n\t\tOptions Indexes MultiViews\n\t\tAllowOverride None\n\t\tOrder allow,deny\n\t\tallow from all\n\t\n\t\n\t\n\t\tDAV On\n\t\tAuthType Basic\n\t\tAuthName \"Restricted Files\"\n\t\tAuthUserFile /etc/apache2/access\n\t\t\n\t\t\tRequire valid-user\n\t\t\n\t\n\t\r\t# see also: trunk/source/kernel/example.htaccess & example.htpasswd\r\t# consider the implications of all of these proxies before hosting a public site\n\tRewriteEngine On\r\tRewriteRule ^/proxy/news.com.com(.*)$ http://news.com.com$1 [P]\r\tRewriteRule ^/proxy/news.cnet.com(.*)$ http://news.cnet.com$1 [P]\r\tRewriteRule ^/proxy/weatherforecastmap.com(.*)$ http://weatherforecastmap.com$1 [P]\r\tRewriteRule ^/proxy/feeds.bbc.co.uk(.*)$ http://feeds.bbc.co.uk$1 [P]\r\tRewriteRule ^/proxy/finance.google.com(.*)$ http://finance.google.com$1 [P]\r\tRewriteRule ^/proxy/download.finance.yahoo.com(.*)$ http://download.finance.yahoo.com$1 [P]\r\tRewriteRule ^/proxy/feeds.feedburner.com(.*)$ http://feeds.feedburner.com$1 [P]\r\tRewriteRule ^/proxy/blogs.sun.com(.*)$ http://blogs.sun.com$1 [P]\r\tRewriteRule ^/proxy/feeds.tuaw.com(.*)$ http://feeds.tuaw.com$1 [P]\r\tRewriteRule ^/proxy/api.flickr.com/services/rest/ http://api.flickr.com/services/rest/?api_key=YOUR_KEY [QSA,P]\r\n"]]>
\n\t\n\t\n\t\tOrder deny,allow\n\t\tAllow from all\n\t \n\n\t# see also"]]>
null
null
true
null
null
1
true
null
true
8
false
null
0.1188707280832095
null
Instructions
18
10
false
null
1
null
0
null
httpd.conf
18
4
true
10
null
0
null
null
1000
1.0655497942497596
Wikicontrol
true
null
null
1000
0.05665949727527017
Wikicontrol
true
null
null
1000
1.5455980918041863
Wikicontrol
true
null
null
1000
0.4096550502039905
Wikicontrol
true
null
null
1000
0.10103204897765196
Wikicontrol
true
null
null
1000
0.7302088849530554
Wikicontrol
true
null
null
1000
1.6013567309199985
Wikicontrol
true
null
null
1000
1.5155734205809595
Wikicontrol
true
null
null
1000
0.6772798688887649
Wikicontrol
true
null
null
1000
1.8225913428322436
Wikicontrol
true
null
AboveareinstructionsforpreparingApachetobeabletosaveworldsusingWebDavandalsotobeablesetupproxiesformakingNetRequestsinLively.TheseareprovidedbyaLivelyuserandmaynotbeideal.Atthistimetheyareintendedjusttogetupandrunningandthesecurityneedstoberevisitedbyyourself.
18
0
true
202
true
203
null
8
true
null
null
1
true
true
8
-0.049999999200753306
false
null
1239913096212
178
1
NaN
null
1239913096
null