From 87c2503571bd7f5e753bc16a05bd198fc58d7d7c Mon Sep 17 00:00:00 2001 From: Nathan Dinsmore Date: Wed, 17 Jun 2015 17:11:40 -0400 Subject: Use Object.create(…) instead of new …() for inheritance (faster) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- morphic.js | 58 +++++++++++++++++++++++++++++----------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) (limited to 'morphic.js') diff --git a/morphic.js b/morphic.js index 2850e32..029afd1 100644 --- a/morphic.js +++ b/morphic.js @@ -528,7 +528,7 @@ whose "isTemplate" flag is false, in other words: a non-template. When creating a copy from a template, the copy's - + reactToTemplateCopy is invoked, if it is present. @@ -2171,7 +2171,7 @@ var TextMorph; // Morph inherits from Node: -Morph.prototype = new Node(); +Morph.prototype = Object.create(Node.prototype); Morph.prototype.constructor = Morph; Morph.uber = Node.prototype; @@ -3696,7 +3696,7 @@ Morph.prototype.overlappingImage = function (otherMorph) { // ShadowMorph inherits from Morph: -ShadowMorph.prototype = new Morph(); +ShadowMorph.prototype = Object.create(Morph.prototype); ShadowMorph.prototype.constructor = ShadowMorph; ShadowMorph.uber = Morph.prototype; @@ -3712,7 +3712,7 @@ function ShadowMorph() { // HandleMorph inherits from Morph: -HandleMorph.prototype = new Morph(); +HandleMorph.prototype = Object.create(Morph.prototype); HandleMorph.prototype.constructor = HandleMorph; HandleMorph.uber = Morph.prototype; @@ -3967,7 +3967,7 @@ var PenMorph; // PenMorph inherits from Morph: -PenMorph.prototype = new Morph(); +PenMorph.prototype = Object.create(Morph.prototype); PenMorph.prototype.constructor = PenMorph; PenMorph.uber = Morph.prototype; @@ -4199,7 +4199,7 @@ var ColorPaletteMorph; // ColorPaletteMorph inherits from Morph: -ColorPaletteMorph.prototype = new Morph(); +ColorPaletteMorph.prototype = Object.create(Morph.prototype); ColorPaletteMorph.prototype.constructor = ColorPaletteMorph; ColorPaletteMorph.uber = Morph.prototype; @@ -4331,7 +4331,7 @@ var GrayPaletteMorph; // GrayPaletteMorph inherits from ColorPaletteMorph: -GrayPaletteMorph.prototype = new ColorPaletteMorph(); +GrayPaletteMorph.prototype = Object.create(ColorPaletteMorph.prototype); GrayPaletteMorph.prototype.constructor = GrayPaletteMorph; GrayPaletteMorph.uber = ColorPaletteMorph.prototype; @@ -4362,7 +4362,7 @@ GrayPaletteMorph.prototype.drawNew = function () { // ColorPickerMorph inherits from Morph: -ColorPickerMorph.prototype = new Morph(); +ColorPickerMorph.prototype = Object.create(Morph.prototype); ColorPickerMorph.prototype.constructor = ColorPickerMorph; ColorPickerMorph.uber = Morph.prototype; @@ -4431,7 +4431,7 @@ var BlinkerMorph; // BlinkerMorph inherits from Morph: -BlinkerMorph.prototype = new Morph(); +BlinkerMorph.prototype = Object.create(Morph.prototype); BlinkerMorph.prototype.constructor = BlinkerMorph; BlinkerMorph.uber = Morph.prototype; @@ -4464,7 +4464,7 @@ var CursorMorph; // CursorMorph inherits from BlinkerMorph: -CursorMorph.prototype = new BlinkerMorph(); +CursorMorph.prototype = Object.create(BlinkerMorph.prototype); CursorMorph.prototype.constructor = CursorMorph; CursorMorph.uber = BlinkerMorph.prototype; @@ -4884,7 +4884,7 @@ var BoxMorph; // BoxMorph inherits from Morph: -BoxMorph.prototype = new Morph(); +BoxMorph.prototype = Object.create(Morph.prototype); BoxMorph.prototype.constructor = BoxMorph; BoxMorph.uber = Morph.prototype; @@ -5092,7 +5092,7 @@ var SpeechBubbleMorph; // SpeechBubbleMorph inherits from BoxMorph: -SpeechBubbleMorph.prototype = new BoxMorph(); +SpeechBubbleMorph.prototype = Object.create(BoxMorph.prototype); SpeechBubbleMorph.prototype.constructor = SpeechBubbleMorph; SpeechBubbleMorph.uber = BoxMorph.prototype; @@ -5392,7 +5392,7 @@ var CircleBoxMorph; // CircleBoxMorph inherits from Morph: -CircleBoxMorph.prototype = new Morph(); +CircleBoxMorph.prototype = Object.create(Morph.prototype); CircleBoxMorph.prototype.constructor = CircleBoxMorph; CircleBoxMorph.uber = Morph.prototype; @@ -5512,7 +5512,7 @@ var SliderButtonMorph; // SliderButtonMorph inherits from CircleBoxMorph: -SliderButtonMorph.prototype = new CircleBoxMorph(); +SliderButtonMorph.prototype = Object.create(CircleBoxMorph.prototype); SliderButtonMorph.prototype.constructor = SliderButtonMorph; SliderButtonMorph.uber = CircleBoxMorph.prototype; @@ -5724,7 +5724,7 @@ SliderButtonMorph.prototype.mouseMove = function () { // SliderMorph inherits from CircleBoxMorph: -SliderMorph.prototype = new CircleBoxMorph(); +SliderMorph.prototype = Object.create(CircleBoxMorph.prototype); SliderMorph.prototype.constructor = SliderMorph; SliderMorph.uber = CircleBoxMorph.prototype; @@ -6092,7 +6092,7 @@ var MouseSensorMorph; // MouseSensorMorph inherits from BoxMorph: -MouseSensorMorph.prototype = new BoxMorph(); +MouseSensorMorph.prototype = Object.create(BoxMorph.prototype); MouseSensorMorph.prototype.constructor = MouseSensorMorph; MouseSensorMorph.uber = BoxMorph.prototype; @@ -6166,7 +6166,7 @@ var TriggerMorph; // InspectorMorph inherits from BoxMorph: -InspectorMorph.prototype = new BoxMorph(); +InspectorMorph.prototype = Object.create(BoxMorph.prototype); InspectorMorph.prototype.constructor = InspectorMorph; InspectorMorph.uber = BoxMorph.prototype; @@ -6661,7 +6661,7 @@ var MenuItemMorph; // MenuMorph inherits from BoxMorph: -MenuMorph.prototype = new BoxMorph(); +MenuMorph.prototype = Object.create(BoxMorph.prototype); MenuMorph.prototype.constructor = MenuMorph; MenuMorph.uber = BoxMorph.prototype; @@ -6956,7 +6956,7 @@ MenuMorph.prototype.popUpCenteredInWorld = function (world) { // StringMorph inherits from Morph: -StringMorph.prototype = new Morph(); +StringMorph.prototype = Object.create(Morph.prototype); StringMorph.prototype.constructor = StringMorph; StringMorph.uber = Morph.prototype; @@ -7491,7 +7491,7 @@ StringMorph.prototype.disableSelecting = function () { // TextMorph inherits from Morph: -TextMorph.prototype = new Morph(); +TextMorph.prototype = Object.create(Morph.prototype); TextMorph.prototype.constructor = TextMorph; TextMorph.uber = Morph.prototype; @@ -8016,7 +8016,7 @@ TextMorph.prototype.inspectIt = function () { // TriggerMorph inherits from Morph: -TriggerMorph.prototype = new Morph(); +TriggerMorph.prototype = Object.create(Morph.prototype); TriggerMorph.prototype.constructor = TriggerMorph; TriggerMorph.uber = Morph.prototype; @@ -8285,7 +8285,7 @@ var MenuItemMorph; // MenuItemMorph inherits from TriggerMorph: -MenuItemMorph.prototype = new TriggerMorph(); +MenuItemMorph.prototype = Object.create(TriggerMorph.prototype); MenuItemMorph.prototype.constructor = MenuItemMorph; MenuItemMorph.uber = TriggerMorph.prototype; @@ -8444,7 +8444,7 @@ MenuItemMorph.prototype.isSelectedListItem = function () { // Frames inherit from Morph: -FrameMorph.prototype = new Morph(); +FrameMorph.prototype = Object.create(Morph.prototype); FrameMorph.prototype.constructor = FrameMorph; FrameMorph.uber = Morph.prototype; @@ -8638,7 +8638,7 @@ FrameMorph.prototype.keepAllSubmorphsWithin = function () { // ScrollFrameMorph //////////////////////////////////////////////////// -ScrollFrameMorph.prototype = new FrameMorph(); +ScrollFrameMorph.prototype = Object.create(FrameMorph.prototype); ScrollFrameMorph.prototype.constructor = ScrollFrameMorph; ScrollFrameMorph.uber = FrameMorph.prototype; @@ -9013,7 +9013,7 @@ ScrollFrameMorph.prototype.toggleTextLineWrapping = function () { // ListMorph /////////////////////////////////////////////////////////// -ListMorph.prototype = new ScrollFrameMorph(); +ListMorph.prototype = Object.create(ScrollFrameMorph.prototype); ListMorph.prototype.constructor = ListMorph; ListMorph.uber = ScrollFrameMorph.prototype; @@ -9151,7 +9151,7 @@ ListMorph.prototype.setExtent = function (aPoint) { // StringFieldMorph inherit from FrameMorph: -StringFieldMorph.prototype = new FrameMorph(); +StringFieldMorph.prototype = Object.create(FrameMorph.prototype); StringFieldMorph.prototype.constructor = StringFieldMorph; StringFieldMorph.uber = FrameMorph.prototype; @@ -9265,7 +9265,7 @@ var BouncerMorph; // Bouncers inherit from Morph: -BouncerMorph.prototype = new Morph(); +BouncerMorph.prototype = Object.create(Morph.prototype); BouncerMorph.prototype.constructor = BouncerMorph; BouncerMorph.uber = Morph.prototype; @@ -9352,7 +9352,7 @@ BouncerMorph.prototype.step = function () { // HandMorph inherits from Morph: -HandMorph.prototype = new Morph(); +HandMorph.prototype = Object.create(Morph.prototype); HandMorph.prototype.constructor = HandMorph; HandMorph.uber = Morph.prototype; @@ -9987,7 +9987,7 @@ HandMorph.prototype.moveBy = function (delta) { // WorldMorph inherits from FrameMorph: -WorldMorph.prototype = new FrameMorph(); +WorldMorph.prototype = Object.create(FrameMorph.prototype); WorldMorph.prototype.constructor = WorldMorph; WorldMorph.uber = FrameMorph.prototype; -- cgit v1.3.1 From c8cbbeb2ada539d8c71a74a000b1eb329ad26bbe Mon Sep 17 00:00:00 2001 From: Nathan Dinsmore Date: Wed, 17 Jun 2015 18:24:54 -0400 Subject: Optimize HandMorph::morphAtPointer a lot On large morph hierarchies this goes from ~85% CPU usage when moving the mouse around to ~5%. --- morphic.js | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) (limited to 'morphic.js') diff --git a/morphic.js b/morphic.js index 029afd1..88f8c22 100644 --- a/morphic.js +++ b/morphic.js @@ -9392,28 +9392,27 @@ HandMorph.prototype.changed = function () { // HandMorph navigation: -HandMorph.prototype.morphAtPointer = function () { - var morphs = this.world.allChildren().slice(0).reverse(), - myself = this, - result = null; - - morphs.forEach(function (m) { - if (m.visibleBounds().containsPoint(myself.bounds.origin) && - result === null && - m.isVisible && - (m.noticesTransparentClick || - (!m.isTransparentAt(myself.bounds.origin))) && - (!(m instanceof ShadowMorph)) && - m.allParents().every(function (each) { - return each.isVisible; - })) { - result = m; - } - }); - if (result !== null) { - return result; +Morph.prototype.topMorphAt = function (p) { + if (!this.isVisible) return null; + for (var c = this.children, i = c.length; i--;) { + var result = c[i].topMorphAt(p); + if (result) return result; + } + return this.bounds.containsPoint(p) ? this : null; +}; +FrameMorph.prototype.topMorphAt = function (p) { + if (!(this.isVisible && this.bounds.containsPoint(p))) return null; + for (var c = this.children, i = c.length; i--;) { + var result = c[i].topMorphAt(p); + if (result) return result; } - return this.world; + return this; +}; +ShadowMorph.prototype.topMorphAt = function () { + return null; +}; +HandMorph.prototype.morphAtPointer = function () { + return this.world.topMorphAt(this.bounds.origin); }; /* -- cgit v1.3.1 From 7bb046856c765facb348d3f461166599c03103d6 Mon Sep 17 00:00:00 2001 From: Nathan Dinsmore Date: Wed, 17 Jun 2015 18:28:18 -0400 Subject: Remove extra space --- morphic.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'morphic.js') diff --git a/morphic.js b/morphic.js index 88f8c22..aca37ed 100644 --- a/morphic.js +++ b/morphic.js @@ -2899,7 +2899,7 @@ Morph.prototype.fullChanged = function () { }; Morph.prototype.childChanged = function () { - // react to a change in one of my children, + // react to a change in one of my children, // default is to just pass this message on upwards // override this method for Morphs that need to adjust accordingly if (this.parent) { -- cgit v1.3.1 From 1f52f4f1522c4bc7c24fb19ba66aeaff3bf15b64 Mon Sep 17 00:00:00 2001 From: Nathan Dinsmore Date: Wed, 17 Jun 2015 18:50:56 -0400 Subject: Optimize StringMorph::clearSelection() This was taking a very large chunk of CPU time when copying large scripts. --- morphic.js | 1 + 1 file changed, 1 insertion(+) (limited to 'morphic.js') diff --git a/morphic.js b/morphic.js index aca37ed..737e944 100644 --- a/morphic.js +++ b/morphic.js @@ -7402,6 +7402,7 @@ StringMorph.prototype.selectionStartSlot = function () { }; StringMorph.prototype.clearSelection = function () { + if (!this.currentlySelecting && this.startMark === 0 && this.endMark === 0) return; this.currentlySelecting = false; this.startMark = 0; this.endMark = 0; -- cgit v1.3.1 From 81b9245d25cc7a3027f21fe13bcb653a7a552c09 Mon Sep 17 00:00:00 2001 From: Nathan Dinsmore Date: Wed, 17 Jun 2015 19:10:30 -0400 Subject: Optimize copy() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1356.2 ms before => 73.3 ms after (on a large block stack) --- morphic.js | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'morphic.js') diff --git a/morphic.js b/morphic.js index 737e944..013660e 100644 --- a/morphic.js +++ b/morphic.js @@ -1266,18 +1266,16 @@ function copy(target) { } if (target instanceof target.constructor && target.constructor !== Object) { - c = clone(target.constructor.prototype); - for (property in target) { - if (Object.prototype.hasOwnProperty.call(target, property)) { - c[property] = target[property]; - } + c = Object.create(target.constructor.prototype); + var keys = Object.keys(target); + for (var l = keys.length, i = 0; i < l; i++) { + property = keys[i]; + c[property] = target[property]; } } else { c = {}; for (property in target) { - if (!c[property]) { - c[property] = target[property]; - } + c[property] = target[property]; } } return c; -- cgit v1.3.1 From 3e613a44090391854c6126d919797911daab2307 Mon Sep 17 00:00:00 2001 From: Nathan Dinsmore Date: Wed, 17 Jun 2015 19:45:31 -0400 Subject: Fix and greatly simplify copying mechanism Due to an error in the original implementation of updateReferences(), Morph subclasses which stored references to other morphs needed to manually override copyRecordingReferences() to update these references (which should have been done automatically by updateReferences). This change fixes updateReferences() and changes the copying mechanism to use Maps, which are much faster. --- morphic.js | 162 +++++++++++++------------------------------------------------ 1 file changed, 34 insertions(+), 128 deletions(-) (limited to 'morphic.js') diff --git a/morphic.js b/morphic.js index 013660e..6ce8559 100644 --- a/morphic.js +++ b/morphic.js @@ -829,12 +829,11 @@ }; If your new morph stores or references other morphs outside of the - submorph tree in other properties, be sure to also override the - default + submorph tree in other properties, you may need to override the default - copyRecordingReferences() + updateReferences() - method accordingly if you want it to support duplication. + method if you want it to support duplication. (6) development and user modes @@ -1268,7 +1267,7 @@ function copy(target) { target.constructor !== Object) { c = Object.create(target.constructor.prototype); var keys = Object.keys(target); - for (var l = keys.length, i = 0; i < l; i++) { + for (var l = keys.length, i = 0; i < l; ++i) { property = keys[i]; c[property] = target[property]; } @@ -3044,45 +3043,49 @@ Morph.prototype.fullCopy = function () { Other properties are also *shallow* copied, so you must override to deep copy Arrays and (complex) Objects */ - var dict = {}, c; - c = this.copyRecordingReferences(dict); + var map = new Map(), c; + c = this.copyRecordingReferences(map); c.forAllChildren(function (m) { - m.updateReferences(dict); + m.updateReferences(map); }); return c; }; -Morph.prototype.copyRecordingReferences = function (dict) { +Morph.prototype.copyRecordingReferences = function (map) { /* Recursively copy this entire composite morph, recording the correspondence between old and new morphs in the given dictionary. This dictionary will be used to update intra-composite references in the copy. See updateReferences(). - Note: This default implementation copies ONLY morphs in the - submorph hierarchy. If a morph stores morphs in other properties - that it wants to copy, then it should override this method to do so. - The same goes for morphs that contain other complex data that - should be copied when the morph is duplicated. + + Note: This default implementation copies ONLY morphs. If a morph + stores morphs in other properties that it wants to copy, then it + should override this method to do so. The same goes for morphs that + contain other complex data that should be copied when the morph is + duplicated. */ var c = this.copy(); - dict[this] = c; + map.set(this, c); this.children.forEach(function (m) { - c.add(m.copyRecordingReferences(dict)); + c.add(m.copyRecordingReferences(map)); }); return c; }; -Morph.prototype.updateReferences = function (dict) { +Morph.prototype.updateReferences = function (map) { /* Update intra-morph references within a composite morph that has been copied. For example, if a button refers to morph X in the orginal composite then the copy of that button in the new composite should refer to the copy of X in new composite, not the original X. */ - var property; - for (property in this) { - if (this[property] && this[property].isMorph && dict[property]) { - this[property] = dict[property]; + var property, value, reference; + for (var properties = Object.keys(this), l = properties.length, i = 0; i < l; ++i) { + property = properties[i]; + value = this[property]; + if (value && value.isMorph) { + reference = map.get(value); + if (reference) this[property] = reference; } } }; @@ -3921,20 +3924,6 @@ HandleMorph.prototype.mouseLeave = function () { this.changed(); }; -// HandleMorph duplicating: - -HandleMorph.prototype.copyRecordingReferences = function (dict) { - // inherited, see comment in Morph - var c = HandleMorph.uber.copyRecordingReferences.call( - this, - dict - ); - if (c.target && dict[this.target]) { - c.target = (dict[this.target]); - } - return c; -}; - // HandleMorph menu: HandleMorph.prototype.attach = function () { @@ -4258,20 +4247,6 @@ ColorPaletteMorph.prototype.updateTarget = function () { } }; -// ColorPaletteMorph duplicating: - -ColorPaletteMorph.prototype.copyRecordingReferences = function (dict) { - // inherited, see comment in Morph - var c = ColorPaletteMorph.uber.copyRecordingReferences.call( - this, - dict - ); - if (c.target && dict[this.target]) { - c.target = (dict[this.target]); - } - return c; -}; - // ColorPaletteMorph menu: ColorPaletteMorph.prototype.developersMenu = function () { @@ -5838,23 +5813,6 @@ SliderMorph.prototype.updateTarget = function () { } }; -// SliderMorph duplicating: - -SliderMorph.prototype.copyRecordingReferences = function (dict) { - // inherited, see comment in Morph - var c = SliderMorph.uber.copyRecordingReferences.call( - this, - dict - ); - if (c.target && dict[this.target]) { - c.target = (dict[this.target]); - } - if (c.button && dict[this.button]) { - c.button = (dict[this.button]); - } - return c; -}; - // SliderMorph menu: SliderMorph.prototype.developersMenu = function () { @@ -8140,20 +8098,6 @@ TriggerMorph.prototype.createLabel = function () { this.add(this.label); }; -// TriggerMorph duplicating: - -TriggerMorph.prototype.copyRecordingReferences = function (dict) { - // inherited, see comment in Morph - var c = TriggerMorph.uber.copyRecordingReferences.call( - this, - dict - ); - if (c.label && dict[this.label]) { - c.label = (dict[this.label]); - } - return c; -}; - // TriggerMorph action: TriggerMorph.prototype.trigger = function () { @@ -8599,20 +8543,6 @@ FrameMorph.prototype.reactToGrabOf = function () { this.adjustBounds(); }; -// FrameMorph duplicating: - -FrameMorph.prototype.copyRecordingReferences = function (dict) { - // inherited, see comment in Morph - var c = FrameMorph.uber.copyRecordingReferences.call( - this, - dict - ); - if (c.frame && dict[this.scrollFrame]) { - c.frame = (dict[this.scrollFrame]); - } - return c; -}; - // FrameMorph menus: FrameMorph.prototype.developersMenu = function () { @@ -8957,32 +8887,22 @@ ScrollFrameMorph.prototype.mouseScroll = function (y, x) { // ScrollFrameMorph duplicating: -ScrollFrameMorph.prototype.copyRecordingReferences = function (dict) { - // inherited, see comment in Morph - var c = ScrollFrameMorph.uber.copyRecordingReferences.call( - this, - dict - ); - if (c.contents && dict[this.contents]) { - c.contents = (dict[this.contents]); - } - if (c.hBar && dict[this.hBar]) { - c.hBar = (dict[this.hBar]); - c.hBar.action = function (num) { - c.contents.setPosition( - new Point(c.left() - num, c.contents.position().y) +ScrollFrameMorph.prototype.updateReferences = function () { + var self = this; + if (this.hBar) { + this.hBar.action = function (num) { + self.contents.setPosition( + new Point(self.left() - num, self.contents.position().y) ); }; } - if (c.vBar && dict[this.vBar]) { - c.vBar = (dict[this.vBar]); - c.vBar.action = function (num) { - c.contents.setPosition( - new Point(c.contents.position().x, c.top() - num) + if (this.vBar) { + this.vBar.action = function (num) { + self.contents.setPosition( + new Point(self.contents.position().x, self.top() - num) ); }; } - return c; }; // ScrollFrameMorph menu: @@ -9242,20 +9162,6 @@ StringFieldMorph.prototype.mouseClickLeft = function (pos) { } }; -// StringFieldMorph duplicating: - -StringFieldMorph.prototype.copyRecordingReferences = function (dict) { - // inherited, see comment in Morph - var c = StringFieldMorph.uber.copyRecordingReferences.call( - this, - dict - ); - if (c.text && dict[this.text]) { - c.text = (dict[this.text]); - } - return c; -}; - // BouncerMorph //////////////////////////////////////////////////////// // I am a Demo of a stepping custom Morph -- cgit v1.3.1 From d5761b074d0c98c047e7319cf2243a39c7a6a303 Mon Sep 17 00:00:00 2001 From: Nathan Dinsmore Date: Wed, 17 Jun 2015 20:08:59 -0400 Subject: Remove clone function (unused) --- morphic.js | 10 ---------- objects.js | 2 +- 2 files changed, 1 insertion(+), 11 deletions(-) (limited to 'morphic.js') diff --git a/morphic.js b/morphic.js index 6ce8559..e23812a 100644 --- a/morphic.js +++ b/morphic.js @@ -1242,16 +1242,6 @@ function getDocumentPositionOf(aDOMelement) { return pos; } -function clone(target) { - // answer a new instance of target's type - if (typeof target === 'object') { - var Clone = function () {nop(); }; - Clone.prototype = target; - return new Clone(); - } - return target; -} - function copy(target) { // answer a shallow copy of target var value, c, property; diff --git a/objects.js b/objects.js index 462aed2..8d11ff6 100644 --- a/objects.js +++ b/objects.js @@ -105,7 +105,7 @@ InspectorMorph, ListMorph, Math, MenuItemMorph, MenuMorph, Morph, MorphicPreferences, MouseSensorMorph, Node, Object, PenMorph, Point, Rectangle, ScrollFrameMorph, ShadowMorph, SliderButtonMorph, SliderMorph, String, StringFieldMorph, StringMorph, TextMorph, -TriggerMorph, WorldMorph, clone, contains, copy, degrees, detect, +TriggerMorph, WorldMorph, contains, copy, degrees, detect, document, getDocumentPositionOf, isNaN, isObject, isString, newCanvas, nop, parseFloat, radians, standardSettings, touchScreenSettings, useBlurredShadows, version, window, modules, IDE_Morph, VariableDialogMorph, -- cgit v1.3.1 From 6a82960cf77f9eac16c9a1ce13f360d80469799d Mon Sep 17 00:00:00 2001 From: Nathan Dinsmore Date: Wed, 17 Jun 2015 20:46:54 -0400 Subject: Optimize moveBy and friends This results in realtime speed for dragging where I used to get 10-15 fps before this change. --- morphic.js | 43 +++++++++---------------------------------- objects.js | 2 +- 2 files changed, 10 insertions(+), 35 deletions(-) (limited to 'morphic.js') diff --git a/morphic.js b/morphic.js index e23812a..5cc4062 100644 --- a/morphic.js +++ b/morphic.js @@ -2398,19 +2398,16 @@ Morph.prototype.visibleBounds = function () { // Morph accessing - simple changes: Morph.prototype.moveBy = function (delta) { - this.changed(); - this.bounds = this.bounds.translateBy(delta); - this.children.forEach(function (child) { - child.moveBy(delta); - }); - this.changed(); + this.fullChanged(); + this.silentMoveBy(delta); + this.fullChanged(); }; Morph.prototype.silentMoveBy = function (delta) { this.bounds = this.bounds.translateBy(delta); - this.children.forEach(function (child) { - child.silentMoveBy(delta); - }); + for (var children = this.children, i = children.length; i--;) { + children[i].silentMoveBy(delta); + } }; Morph.prototype.setPosition = function (aPoint) { @@ -3146,9 +3143,7 @@ Morph.prototype.slideBackTo = function (situation, inSteps) { this.fps = 0; this.step = function () { - myself.fullChanged(); - myself.silentMoveBy(new Point(xStep, yStep)); - myself.fullChanged(); + myself.moveBy(new Point(xStep, yStep)); stepCount += 1; if (stepCount === steps) { situation.origin.add(myself); @@ -8433,17 +8428,6 @@ FrameMorph.prototype.fullDrawOn = function (aCanvas, aRect) { }); }; -// FrameMorph scrolling optimization: - -FrameMorph.prototype.moveBy = function (delta) { - this.changed(); - this.bounds = this.bounds.translateBy(delta); - this.children.forEach(function (child) { - child.silentMoveBy(delta); - }); - this.changed(); -}; - // FrameMorph scrolling support: FrameMorph.prototype.submorphBounds = function () { @@ -9274,7 +9258,8 @@ HandMorph.prototype.init = function (aWorld) { this.contextMenuEnabled = false; }; -HandMorph.prototype.changed = function () { +HandMorph.prototype.changed = +HandMorph.prototype.fullChanged = function () { var b; if (this.world !== null) { b = this.fullBounds(); @@ -9282,7 +9267,6 @@ HandMorph.prototype.changed = function () { this.world.broken.push(this.fullBounds().spread()); } } - }; // HandMorph navigation: @@ -9865,15 +9849,6 @@ HandMorph.prototype.destroyTemporaries = function () { }); }; -// HandMorph dragging optimization - -HandMorph.prototype.moveBy = function (delta) { - Morph.prototype.trackChanges = false; - HandMorph.uber.moveBy.call(this, delta); - Morph.prototype.trackChanges = true; - this.fullChanged(); -}; - // WorldMorph ////////////////////////////////////////////////////////// diff --git a/objects.js b/objects.js index 8d11ff6..1e8060e 100644 --- a/objects.js +++ b/objects.js @@ -3290,7 +3290,7 @@ SpriteMorph.prototype.nestingBounds = function () { // SpriteMorph motion primitives -Morph.prototype.setPosition = function (aPoint, justMe) { +SpriteMorph.prototype.setPosition = function (aPoint, justMe) { // override the inherited default to make sure my parts follow // unless it's justMe var delta = aPoint.subtract(this.topLeft()); -- cgit v1.3.1 From f20ea6792688b97a8d9b50e9eed783e4773d9a86 Mon Sep 17 00:00:00 2001 From: Nathan Dinsmore Date: Wed, 17 Jun 2015 21:08:12 -0400 Subject: Provide a fallback for browsers that don't support Map --- morphic.js | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'morphic.js') diff --git a/morphic.js b/morphic.js index 5cc4062..fe2cc8b 100644 --- a/morphic.js +++ b/morphic.js @@ -3077,6 +3077,40 @@ Morph.prototype.updateReferences = function (map) { } }; +if (typeof Map === 'undefined') { + // use normal objects + stringification if Map doesn't exist + + Morph.prototype.fullCopy = function () { + var dict = {}, c; + c = this.copyRecordingReferences(dict); + c.forAllChildren(function (m) { + m.updateReferences(dict); + }); + return c; + }; + + Morph.prototype.copyRecordingReferences = function (dict) { + var c = this.copy(); + dict[this] = c; + this.children.forEach(function (m) { + c.add(m.copyRecordingReferences(dict)); + }); + return c; + }; + + Morph.prototype.updateReferences = function (dict) { + var property, value, reference; + for (var properties = Object.keys(this), l = properties.length, i = 0; i < l; ++i) { + property = properties[i]; + value = this[property]; + if (value && value.isMorph) { + reference = dict[value]; + if (reference) this[property] = reference; + } + } + }; +} + // Morph dragging and dropping: Morph.prototype.rootForGrab = function () { -- cgit v1.3.1 From aaa3c407bc54e61ba2944f97956a89bf76ad22d1 Mon Sep 17 00:00:00 2001 From: Nathan Dinsmore Date: Wed, 17 Jun 2015 21:11:04 -0400 Subject: Add a missing super call in overridden updateReferences() --- morphic.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'morphic.js') diff --git a/morphic.js b/morphic.js index fe2cc8b..0b20eb5 100644 --- a/morphic.js +++ b/morphic.js @@ -8895,8 +8895,9 @@ ScrollFrameMorph.prototype.mouseScroll = function (y, x) { // ScrollFrameMorph duplicating: -ScrollFrameMorph.prototype.updateReferences = function () { +ScrollFrameMorph.prototype.updateReferences = function (map) { var self = this; + ScrollFrameMorph.uber.updateReferences.call(this, map); if (this.hBar) { this.hBar.action = function (num) { self.contents.setPosition( -- cgit v1.3.1 From 0245a81fc04cf787bae2f3467f850a07d9791ca7 Mon Sep 17 00:00:00 2001 From: Nathan Dinsmore Date: Wed, 17 Jun 2015 23:05:34 -0400 Subject: Optimize loading projects a bit more --- blocks.js | 63 ++++++++++++++++++++++++++++++++------------------------------ morphic.js | 6 +++--- 2 files changed, 36 insertions(+), 33 deletions(-) (limited to 'morphic.js') diff --git a/blocks.js b/blocks.js index 6c10354..8aad311 100644 --- a/blocks.js +++ b/blocks.js @@ -1421,16 +1421,19 @@ SyntaxElementMorph.prototype.labelPart = function (spec) { new Point() : this.embossing; part.drawNew(); } else { - part = new StringMorph(spec); - part.fontName = this.labelFontName; - part.fontStyle = this.labelFontStyle; - part.fontSize = this.fontSize; - part.color = new Color(255, 255, 255); - part.isBold = true; - part.shadowColor = this.color.darker(this.labelContrast); - part.shadowOffset = MorphicPreferences.isFlat ? - new Point() : this.embossing; - part.drawNew(); + part = new StringMorph( + spec, // text + this.fontSize, // fontSize + this.labelFontStyle, // fontStyle + true, // bold + false, // italic + false, // isNumeric + MorphicPreferences.isFlat ? + new Point() : this.embossing, // shadowOffset + this.color.darker(this.labelContrast), // shadowColor + new Color(255, 255, 255), // color + this.labelFontName // fontName + ); } return part; }; @@ -6918,7 +6921,8 @@ InputSlotMorph.prototype.setChoices = function (dict, readonly) { // InputSlotMorph layout: InputSlotMorph.prototype.fixLayout = function () { - var contents = this.contents(), + var width, height, arrowWidth, + contents = this.contents(), arrow = this.arrow(); contents.isNumeric = this.isNumeric; @@ -6935,32 +6939,29 @@ InputSlotMorph.prototype.fixLayout = function () { arrow.setSize(this.fontSize); arrow.show(); } else { - arrow.setSize(0); arrow.hide(); } - this.setHeight( - contents.height() - + this.edge * 2 - // + this.typeInPadding * 2 - ); + arrowWidth = arrow.isVisible ? arrow.width() : 0; + + height = contents.height() + this.edge * 2; // + this.typeInPadding * 2 if (this.isNumeric) { - this.setWidth(contents.width() - + Math.floor(arrow.width() * 0.5) + width = contents.width() + + Math.floor(arrowWidth * 0.5) + this.height() - + this.typeInPadding * 2 - ); + + this.typeInPadding * 2; } else { - this.setWidth(Math.max( + width = Math.max( contents.width() - + arrow.width() + + arrowWidth + this.edge * 2 + this.typeInPadding * 2, contents.rawHeight ? // single vs. multi-line contents - contents.rawHeight() + arrow.width() - : contents.height() / 1.2 + arrow.width(), + contents.rawHeight() + arrowWidth + : contents.height() / 1.2 + arrowWidth, this.minWidth // for text-type slots - )); + ); } + this.setExtent(new Point(width, height)); if (this.isNumeric) { contents.setPosition(new Point( Math.floor(this.height() / 2), @@ -6973,10 +6974,12 @@ InputSlotMorph.prototype.fixLayout = function () { ).add(new Point(this.typeInPadding, 0)).add(this.position())); } - arrow.setPosition(new Point( - this.right() - arrow.width() - this.edge, - contents.top() - )); + if (arrow.isVisible) { + arrow.setPosition(new Point( + this.right() - arrowWidth - this.edge, + contents.top() + )); + } if (this.parent) { if (this.parent.fixLayout) { diff --git a/morphic.js b/morphic.js index 0b20eb5..1552d78 100644 --- a/morphic.js +++ b/morphic.js @@ -2199,7 +2199,7 @@ function Morph() { // Morph initialization: -Morph.prototype.init = function () { +Morph.prototype.init = function (noDraw) { Morph.uber.init.call(this); this.isMorph = true; this.bounds = new Rectangle(0, 0, 50, 40); @@ -2212,7 +2212,7 @@ Morph.prototype.init = function () { this.isTemplate = false; this.acceptsDrops = false; this.noticesTransparentClick = false; - this.drawNew(); + if (!noDraw) this.drawNew(); this.fps = 0; this.customContextMenu = null; this.lastTime = Date.now(); @@ -6999,7 +6999,7 @@ StringMorph.prototype.init = function ( this.markedBackgoundColor = new Color(60, 60, 120); // initialize inherited properties: - StringMorph.uber.init.call(this); + StringMorph.uber.init.call(this, true); // override inherited properites: this.color = color || new Color(0, 0, 0); -- cgit v1.3.1 From 82197c7a8c1835048fb6a787c41845049c89ca0c Mon Sep 17 00:00:00 2001 From: Nathan Dinsmore Date: Thu, 18 Jun 2015 15:33:25 -0400 Subject: Declare some variables earlier --- morphic.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'morphic.js') diff --git a/morphic.js b/morphic.js index 1552d78..9f6e8c2 100644 --- a/morphic.js +++ b/morphic.js @@ -1244,7 +1244,7 @@ function getDocumentPositionOf(aDOMelement) { function copy(target) { // answer a shallow copy of target - var value, c, property; + var value, c, property, keys, l, i; if (typeof target !== 'object') { return target; @@ -1256,8 +1256,8 @@ function copy(target) { if (target instanceof target.constructor && target.constructor !== Object) { c = Object.create(target.constructor.prototype); - var keys = Object.keys(target); - for (var l = keys.length, i = 0; i < l; ++i) { + keys = Object.keys(target); + for (l = keys.length, i = 0; i < l; ++i) { property = keys[i]; c[property] = target[property]; } -- cgit v1.3.1 From bae22f08044f01adba15608d3885e84e173c8889 Mon Sep 17 00:00:00 2001 From: Nathan Dinsmore Date: Thu, 18 Jun 2015 15:33:36 -0400 Subject: Add braces --- morphic.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'morphic.js') diff --git a/morphic.js b/morphic.js index 9f6e8c2..01220d5 100644 --- a/morphic.js +++ b/morphic.js @@ -2212,7 +2212,7 @@ Morph.prototype.init = function (noDraw) { this.isTemplate = false; this.acceptsDrops = false; this.noticesTransparentClick = false; - if (!noDraw) this.drawNew(); + if (!noDraw) { this.drawNew(); } this.fps = 0; this.customContextMenu = null; this.lastTime = Date.now(); -- cgit v1.3.1 From bc79cd8df2b6c62a80bb6850697d368cf9acebd5 Mon Sep 17 00:00:00 2001 From: Nathan Dinsmore Date: Thu, 18 Jun 2015 15:33:44 -0400 Subject: Call isTransparentAt --- morphic.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'morphic.js') diff --git a/morphic.js b/morphic.js index 01220d5..aa731ab 100644 --- a/morphic.js +++ b/morphic.js @@ -9312,7 +9312,7 @@ Morph.prototype.topMorphAt = function (p) { var result = c[i].topMorphAt(p); if (result) return result; } - return this.bounds.containsPoint(p) ? this : null; + return this.bounds.containsPoint(p) && (this.noticesTransparentClick || !this.isTransparentAt(p)) ? this : null; }; FrameMorph.prototype.topMorphAt = function (p) { if (!(this.isVisible && this.bounds.containsPoint(p))) return null; @@ -9320,13 +9320,13 @@ FrameMorph.prototype.topMorphAt = function (p) { var result = c[i].topMorphAt(p); if (result) return result; } - return this; + return this.noticesTransparentClick || !this.isTransparentAt(p) ? this : null; }; ShadowMorph.prototype.topMorphAt = function () { return null; }; HandMorph.prototype.morphAtPointer = function () { - return this.world.topMorphAt(this.bounds.origin); + return this.world.topMorphAt(this.bounds.origin) || this.world; }; /* @@ -9913,6 +9913,7 @@ WorldMorph.prototype.init = function (aCanvas, fillPage) { this.isDraggable = false; this.currentKey = null; // currently pressed key code this.worldCanvas = aCanvas; + this.noticesTransparentClick = true; // additional properties: this.stamp = Date.now(); // reference in multi-world setups -- cgit v1.3.1 From aef56afebf1504a9ce09ee5668d00c3c1df17565 Mon Sep 17 00:00:00 2001 From: Nathan Dinsmore Date: Thu, 25 Jun 2015 02:30:06 -0400 Subject: Use associative arrays if Map doesn't exist --- morphic.js | 143 ++++++++++++++++++++++++++++++------------------------------- 1 file changed, 70 insertions(+), 73 deletions(-) (limited to 'morphic.js') diff --git a/morphic.js b/morphic.js index aa731ab..e4df8f0 100644 --- a/morphic.js +++ b/morphic.js @@ -3015,101 +3015,98 @@ Morph.prototype.isTransparentAt = function (aPoint) { // Morph duplicating: -Morph.prototype.copy = function () { - var c = copy(this); - c.parent = null; - c.children = []; - c.bounds = this.bounds.copy(); - return c; -}; - -Morph.prototype.fullCopy = function () { - /* - Produce a copy of me with my entire tree of submorphs. Morphs - mentioned more than once are all directed to a single new copy. - Other properties are also *shallow* copied, so you must override - to deep copy Arrays and (complex) Objects - */ - var map = new Map(), c; - c = this.copyRecordingReferences(map); - c.forAllChildren(function (m) { - m.updateReferences(map); - }); - return c; -}; - -Morph.prototype.copyRecordingReferences = function (map) { - /* - Recursively copy this entire composite morph, recording the - correspondence between old and new morphs in the given dictionary. - This dictionary will be used to update intra-composite references - in the copy. See updateReferences(). - - Note: This default implementation copies ONLY morphs. If a morph - stores morphs in other properties that it wants to copy, then it - should override this method to do so. The same goes for morphs that - contain other complex data that should be copied when the morph is - duplicated. - */ - var c = this.copy(); - map.set(this, c); - this.children.forEach(function (m) { - c.add(m.copyRecordingReferences(map)); - }); - return c; -}; - -Morph.prototype.updateReferences = function (map) { - /* - Update intra-morph references within a composite morph that has - been copied. For example, if a button refers to morph X in the - orginal composite then the copy of that button in the new composite - should refer to the copy of X in new composite, not the original X. - */ - var property, value, reference; - for (var properties = Object.keys(this), l = properties.length, i = 0; i < l; ++i) { - property = properties[i]; - value = this[property]; - if (value && value.isMorph) { - reference = map.get(value); - if (reference) this[property] = reference; - } - } -}; - -if (typeof Map === 'undefined') { - // use normal objects + stringification if Map doesn't exist +(function () { + // don't overwrite the global Map with AssociativeMap + var Map = window.Map || AssociativeMap; + + Morph.prototype.copy = function () { + var c = copy(this); + c.parent = null; + c.children = []; + c.bounds = this.bounds.copy(); + return c; + }; Morph.prototype.fullCopy = function () { - var dict = {}, c; - c = this.copyRecordingReferences(dict); + /* + Produce a copy of me with my entire tree of submorphs. Morphs + mentioned more than once are all directed to a single new copy. + Other properties are also *shallow* copied, so you must override + to deep copy Arrays and (complex) Objects + */ + var map = new Map(), c; + c = this.copyRecordingReferences(map); c.forAllChildren(function (m) { - m.updateReferences(dict); + m.updateReferences(map); }); return c; }; - Morph.prototype.copyRecordingReferences = function (dict) { + Morph.prototype.copyRecordingReferences = function (map) { + /* + Recursively copy this entire composite morph, recording the + correspondence between old and new morphs in the given dictionary. + This dictionary will be used to update intra-composite references + in the copy. See updateReferences(). + + Note: This default implementation copies ONLY morphs. If a morph + stores morphs in other properties that it wants to copy, then it + should override this method to do so. The same goes for morphs that + contain other complex data that should be copied when the morph is + duplicated. + */ var c = this.copy(); - dict[this] = c; + map.set(this, c); this.children.forEach(function (m) { - c.add(m.copyRecordingReferences(dict)); + c.add(m.copyRecordingReferences(map)); }); return c; }; - Morph.prototype.updateReferences = function (dict) { + Morph.prototype.updateReferences = function (map) { + /* + Update intra-morph references within a composite morph that has + been copied. For example, if a button refers to morph X in the + orginal composite then the copy of that button in the new composite + should refer to the copy of X in new composite, not the original X. + */ var property, value, reference; for (var properties = Object.keys(this), l = properties.length, i = 0; i < l; ++i) { property = properties[i]; value = this[property]; if (value && value.isMorph) { - reference = dict[value]; + reference = map.get(value); if (reference) this[property] = reference; } } }; -} + + // associative array fallback for Map + + function AssociativeMap() { + this.keys = []; + this.values = []; + } + + AssociativeMap.prototype.get = function(key) { + for (var keys = this.keys, i = keys.length; i--;) { + if (keys[i] === key) return this.values[i]; + } + return undefined; + }; + + AssociativeMap.prototype.set = function(key, value) { + for (var keys = this.keys, i = keys.length; i--;) { + if (keys[i] === key) { + this.values[i] = value; + return; + } + } + keys.push(key); + this.values.push(value); + }; + +}()); // Morph dragging and dropping: -- cgit v1.3.1