Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge pull request #253 from enyojs/bindSafely

add enyo.Object.bindSafely and core core to use it in preference to enyo.bind
  • Loading branch information...
commit f7702ebaeef014568aa7f7313b33443fe87f8644 2 parents b2f05ea + 0dbe797
@clinuz clinuz authored
View
29 samples/GestureSample.js
@@ -4,7 +4,7 @@ enyo.kind({
classes: "gesture-sample enyo-fit enyo-unselectable",
components: [
{
- classes:"gesture-sample-pad",
+ classes:"gesture-sample-pad",
fit:true,
ondown: "handleEvent",
onup: "handleEvent",
@@ -54,9 +54,12 @@ enyo.kind({
this.inherited(arguments);
this.eventList = {};
this.eventCount = 0;
- enyo.forEach(["All events","down","up","tap","move","enter","leave","dragstart","drag","dragover","hold","release","holdpulse","flick","gesturestart","gesturechange","gestureend"], enyo.bind(this, function(event) {
- this.$.eventPicker.createComponent({content:event, style:"text-align:left"});
- }));
+ enyo.forEach(
+ ["All events","down","up","tap","move","enter","leave","dragstart","drag","dragover","hold","release",
+ "holdpulse","flick","gesturestart","gesturechange","gestureend"],
+ this.bindSafely(function(event) {
+ this.$.eventPicker.createComponent({content:event, style:"text-align:left"});
+ }));
},
handleEvent: function(inSender, inEvent) {
var event = enyo.clone(inEvent);
@@ -65,13 +68,13 @@ enyo.kind({
}
var eventItem = this.eventList[event.type];
if (eventItem) {
- eventItem.setEvent(event);
+ eventItem.set("event", event, true);
} else {
this.eventCount++;
eventItem = this.$.eventList.createComponent({
kind: "enyo.sample.EventItem",
event:event,
- truncate: this.$.truncateDetail.getValue(),
+ truncate: this.$.truncateDetail.get("value"),
persist: this.monitorEvent
});
this.eventList[event.type] = eventItem;
@@ -83,7 +86,7 @@ enyo.kind({
},
truncateChanged: function() {
for (var i in this.eventList) {
- this.eventList[i].setTruncate(this.$.truncateDetail.getValue());
+ this.eventList[i].set("truncate", this.$.truncateDetail.get("value"));
}
this.reflow();
return false;
@@ -108,7 +111,7 @@ enyo.kind({
this.reflow();
},
toggleSettings: function() {
- this.$.settings.setShowing(!this.$.settings.getShowing());
+ this.$.settings.set("showing", !this.$.settings.get("showing"));
this.reflow();
},
preventDefault: function() {
@@ -147,10 +150,6 @@ enyo.kind({
truncateChanged: function() {
this.$.eventProps.addRemoveClass("gesture-sample-truncate", this.truncate);
},
- // since event is an object, force set
- setEvent: function(inEvent) {
- this.setPropertyValue("event", inEvent, "eventChanged");
- },
eventChanged: function(inOld) {
if (this.event) {
if (this.timeout) {
@@ -158,7 +157,7 @@ enyo.kind({
this.timeout = null;
}
this.$.animator.stop();
- this.$.eventProps.setContent(this.getPropsString());
+ this.$.eventProps.set("content", this.getPropsString());
this.$.animator.play();
}
},
@@ -168,7 +167,7 @@ enyo.kind({
},
animationEnded: function() {
if (!this.persist) {
- this.timeout = setTimeout(enyo.bind(this, function() {
+ this.timeout = setTimeout(this.bindSafely(function() {
this.doDone({type:this.event.type});
}), 2000);
}
@@ -185,7 +184,7 @@ enyo.kind({
for (var i in this.event) {
if ((this.event[i] !== undefined) &&
(this.event[i] !== null) &&
- !(this.event[i] instanceof Object) &&
+ !(this.event[i] instanceof Object) &&
(i != "type")) {
props.push(i + ": " + this.event[i]);
}
View
2  source/ajax/Ajax.js
@@ -123,7 +123,7 @@ enyo.kind({
this.xhr = enyo.xhr.request({
url: url,
method: this.method,
- callback: enyo.bind(this, "receive"),
+ callback: this.bindSafely("receive"),
body: body,
headers: xhr_headers,
sync: window.PalmSystem ? false : this.sync,
View
6 source/ajax/Async.js
@@ -56,11 +56,11 @@ enyo.kind({
},
//* @protected
route: function(inAsync, inValue) {
- var r = enyo.bind(this, "respond");
+ var r = this.bindSafely("respond");
inAsync.response(function(inSender, inValue) {
r(inValue);
});
- var f = enyo.bind(this, "fail");
+ var f = this.bindSafely("fail");
inAsync.error(function(inSender, inValue) {
f(inValue);
});
@@ -84,7 +84,7 @@ enyo.kind({
startTimer: function() {
this.startTime = enyo.now();
if (this.timeout) {
- this.timeoutJob = setTimeout(enyo.bind(this, "timeoutComplete"), this.timeout);
+ this.timeoutJob = setTimeout(this.bindSafely("timeoutComplete"), this.timeout);
}
},
endTimer: function() {
View
6 source/ajax/Jsonp.js
@@ -48,7 +48,7 @@ enyo.kind({
script.charset = this.charset;
}
// most modern browsers also have an onerror handler
- script.onerror = enyo.bind(this, function() {
+ script.onerror = this.bindSafely(function() {
// we don't get an error code, so we'll just use the generic 400 error status
this.fail(400);
});
@@ -84,10 +84,10 @@ enyo.kind({
this.src = this.buildUrl(inParams, callbackFunctionName);
this.addScriptElement();
//
- window[callbackFunctionName] = enyo.bind(this, this.respond);
+ window[callbackFunctionName] = this.bindSafely(this.respond);
//
// setup cleanup handlers for JSONP completion and failure
- var cleanup = enyo.bind(this, function() {
+ var cleanup = this.bindSafely(function() {
this.removeScriptElement();
window[callbackFunctionName] = null;
});
View
4 source/kernel/Component.js
@@ -473,7 +473,7 @@ enyo.kind({
this._silenced = true;
this._silence_count += 1;
},
-
+
//*@public
/**
If the internal silence counter is 0 this method will allow
@@ -500,7 +500,7 @@ enyo.kind({
}
// stop any existing jobs with same name
this.stopJob(inJobName);
- this.__jobs[inJobName] = setTimeout(enyo.bind(this, function() {
+ this.__jobs[inJobName] = setTimeout(this.bindSafely(function() {
this.stopJob(inJobName);
// call "inJob" with this bound to the component.
inJob.call(this);
View
44 source/kernel/Object.js
@@ -36,7 +36,7 @@ enyo.kind({
enyo._objectCount++;
this.importProps(props);
},
-
+
importProps: function (props) {
if (props) {
for (var key in props) {
@@ -99,7 +99,7 @@ enyo.kind({
a string the object will attempt to be resolved. The goal is
to determine of the the property is a constructor, an instance or
nothing. See _lang.js#enyo.findAndInstance_ for more information.
-
+
If a method exists of the form `{property}FindAndInstance` it will
be used as the callback accepting two parameters, the constructor
if it was found and the instance if it was found or created,
@@ -113,7 +113,7 @@ enyo.kind({
// go ahead and call the enyo scoped version of this method
return enyo.findAndInstance.call(this, property, fn, this);
},
-
+
//*@public
/**
Call this method with the name (or path) to the desired property or
@@ -121,7 +121,7 @@ enyo.kind({
the value of that property and not the function. If it cannot find
or resolve the requested path relative to the object it will return
undefined.
-
+
This method is backwards compatible and will automatically call any
existing _getter_ method that uses the getProperty convention although
this convention ought to be replaced using a computed property moving
@@ -137,7 +137,7 @@ enyo.kind({
been changed if the values are not the same. If the property it finds
is a computed property it will pass the intended value to the computed
property (but will not return the value).
-
+
This method is backwards compatible and will call any setter of the
setProperty convention although these methods should be replaced with
computed properties or observers where necessary.
@@ -146,6 +146,34 @@ enyo.kind({
return enyo.setPath.apply(this, arguments);
},
+ //*@public
+ /**
+ Bind a callback to this object. The bound method will be aborted cleanly with no
+ return value if the object has been destroyed. This usually should be used instead
+ of `enyo.bind` for running code in the context of a enyo.Object-derivative.
+ */
+ bindSafely: function(method/*, bound arguments*/) {
+ var scope = this;
+ if (enyo.isString(method)) {
+ if (this[method]) {
+ method = this[method];
+ } else {
+ throw(['enyo.Object.bindSafely: this["', method, '"] is null (this="', this, '")'].join(''));
+ }
+ }
+ if (enyo.isFunction(method)) {
+ var args = enyo.cloneArray(arguments, 2);
+ return function() {
+ if (scope.destroyed) {
+ return;
+ }
+ var nargs = enyo.cloneArray(arguments);
+ return method.apply(scope, args.concat(nargs));
+ };
+ } else {
+ throw(['enyo.Object.bindSafely: this["', method, '"] is not a function (this="', this, '")'].join(''));
+ }
+ },
//*@protected
destroy: function () {
// JS objects are never truly destroyed (GC'd) until all references are gone,
@@ -153,7 +181,7 @@ enyo.kind({
// to this flag.
this.destroyed = true;
},
-
+
_is_object: true
});
@@ -219,7 +247,7 @@ enyo.Object.addGetterSetter = function (property, value, proto) {
fn = proto[getter];
// if there isn't already a getter provided create one
if ("function" !== typeof fn) {
- fn = proto[getter] = function () {return this.get(property)};
+ fn = proto[getter] = function () {return this.get(property);};
fn.overloaded = false;
} else if (false !== fn.overloaded) {
// otherwise we need to mark it as having been overloaded
@@ -229,7 +257,7 @@ enyo.Object.addGetterSetter = function (property, value, proto) {
// if there isn't already a setter provided create one
fn = proto[setter];
if ("function" !== typeof fn) {
- fn = proto[setter] = function () {return this.set(property, arguments[0])};
+ fn = proto[setter] = function () {return this.set(property, arguments[0]);};
fn.overloaded = false;
} else if (false !== fn.overloaded) {
// otherwise we need to mark it as having been overloaded
View
8 source/kernel/Oop.js
@@ -79,12 +79,12 @@ enyo.kind = function(inProps) {
// create our prototype
//ctor.prototype = isa ? enyo.delegate(isa) : {};
enyo.setPrototype(ctor, isa ? enyo.delegate(isa) : {});
-
+
// there are special cases where a base class has a property
// that may need to be concatenated with a subclasses implementation
// as opposed to completely overwriting it...
enyo.handleConcatenatedProperties(ctor.prototype, inProps);
-
+
// put in our props
enyo.mixin(ctor.prototype, inProps);
// alias class name as 'kind' in the prototype
@@ -141,7 +141,7 @@ enyo.singleton = function(conf, context) {
//* @protected
enyo.kind.makeCtor = function() {
- return function() {;
+ return function() {
if (!(this instanceof arguments.callee)) {
throw "enyo.kind: constructor called directly, not using 'new'";
}
@@ -158,7 +158,7 @@ enyo.kind.makeCtor = function() {
// post-constructor initialization
this.constructed.apply(this, arguments);
}
-
+
for (var idx = 0; idx < enyo.kind.postConstructors.length; ++idx) {
enyo.kind.postConstructors[idx].apply(this, cargs);
}
View
2  source/kernel/job.js
@@ -9,7 +9,7 @@
onscroll: function() {
// updateThumb will be called, but only when 1s has elapsed since the
// last onscroll
- enyo.job("updateThumb", enyo.bind(this, "updateThumb"), 1000);
+ enyo.job("updateThumb", this.bindSafely("updateThumb"), 1000);
}
*/
enyo.job = function(inJobName, inJob, inWait) {
View
2  source/touch/ScrollMath.js
@@ -151,7 +151,7 @@ enyo.kind({
// delta tracking
var x0, y0;
// animation handler
- var fn = enyo.bind(this, function() {
+ var fn = this.bindSafely(function() {
// wall-clock time
var t1 = enyo.now();
// schedule next frame
View
2  source/touch/Thumb.js
@@ -102,7 +102,7 @@ enyo.kind({
},
delayHide: function(inDelay) {
if (this.showing) {
- enyo.job(this.id + "hide", enyo.bind(this, "hide"), inDelay || 0);
+ enyo.job(this.id + "hide", this.bindSafely("hide"), inDelay || 0);
}
},
cancelDelayHide: function() {
View
10 source/touch/TransitionScrollStrategy.js
@@ -522,7 +522,7 @@ enyo.kind({
// crossing into the overflow region, and bubble a scroll event
setCSSTransitionInterval: function() {
this.clearCSSTransitionInterval();
- this.scrollInterval = setInterval(enyo.bind(this, function() {
+ this.scrollInterval = setInterval(this.bindSafely(function() {
this.updateScrollPosition();
this.correctOverflow();
}), this.scrollIntervalMS);
@@ -531,7 +531,7 @@ enyo.kind({
// a scroll event (don't check for crossing into overflow since we're there already)
setOverflowTransitionInterval: function() {
this.clearCSSTransitionInterval();
- this.scrollInterval = setInterval(enyo.bind(this, function() {
+ this.scrollInterval = setInterval(this.bindSafely(function() {
this.updateScrollPosition();
}), this.scrollIntervalMS);
},
@@ -575,9 +575,9 @@ enyo.kind({
if(inEvent.originator !== this.$.client) {
return;
}
-
+
var posChanged = false;
-
+
if(this.isInTopOverScroll()) {
posChanged = true;
this.scrollTop = this.topBoundary;
@@ -585,7 +585,7 @@ enyo.kind({
posChanged = true;
this.scrollTop = -1*this.bottomBoundary;
}
-
+
if(this.isInLeftOverScroll()) {
posChanged = true;
this.scrollLeft = this.leftBoundary;
View
2  source/ui/Animator.js
@@ -44,7 +44,7 @@ enyo.kind({
//* @protected
constructed: function() {
this.inherited(arguments);
- this._next = enyo.bind(this, "next");
+ this._next = this.bindSafely("next");
},
destroy: function() {
this.stop();
View
6 tools/test/ajax/tests/AjaxTest.js
@@ -226,7 +226,7 @@ enyo.kind({
})
.error(this, function(inSender, inValue) {
// extra timeout is to make sure that timeout fail code cancels XHR
- enyo.job("timeouttest", enyo.bind(this, function() {this.finish("");}), 4000);
+ enyo.job("timeouttest", this.bindSafely(function() {this.finish("");}), 4000);
})
.go();
},
@@ -236,8 +236,8 @@ enyo.kind({
// getting success means server sent wrong response
return false;
}, function(inError) {
- return (inError === 500) &&
- req.xhrResponse &&
+ return (inError === 500) &&
+ req.xhrResponse &&
(req.xhrResponse.status === 500) &&
(req.xhrResponse.headers['content-type'] === "text/plain; charset=utf-8") &&
(req.xhrResponse.body === "my error description");
View
10 tools/test/ajax/tests/WebServiceTest.js
@@ -16,7 +16,7 @@ enyo.kind({
this._testWebService(enyo.mixin({url: "php/test1.php?format=" + (inProps.format || inProps.handleAs)}, inProps), null, inAssertFn);
},
testJsonResponse: function() {
- this._testResponse({handleAs: "json"},
+ this._testResponse({handleAs: "json"},
function(inValue) {
return inValue.response == "hello";
}
@@ -77,7 +77,7 @@ enyo.kind({
// test CORS (Cross-Origin Resource Sharing) by testing against youtube api
testCORS: function() {
this._testWebService({
- url: "http://query.yahooapis.com/v1/public/yql/jonathan/weather/"},
+ url: "http://query.yahooapis.com/v1/public/yql/jonathan/weather/"},
{q: 'select * from weather.forecast where location=94025', format: "json"},
function(inValue) {
enyo.log(inValue);
@@ -101,11 +101,11 @@ enyo.kind({
},
_timeoutError: function(inSender, inEvent) {
// extra timeout is to make sure that timeout fail code cancels XHR
- enyo.job("wstimeouttest", enyo.bind(this, function() {this.finish("");}), 4000);
+ enyo.job("wstimeouttest", this.bindSafely(function() {this.finish("");}), 4000);
},
testTimeout: function() {
- var ws = this.createComponent({kind: enyo.WebService,
- onResponse: "_timeoutResponse", onError: "_timeoutError"},
+ var ws = this.createComponent({kind: enyo.WebService,
+ onResponse: "_timeoutResponse", onError: "_timeoutError"},
{url: "php/test3.php", timeout: 500});
ws.send();
}
View
24 tools/test/core/test.js
@@ -32,7 +32,7 @@ Test Package Wish list:
Collapse success results for a suite, so large swaths of green don't hide the red.
Expandable stack trace & logging for failures, so they can be collapsed by default.
Support for async beforeEach & afterEach
-Jasmine style assert mechanism, so we can have fancy english text for failures
+Jasmine style assert mechanism, so we can have fancy english text for failures
e.g., this.assert("spreadsheet total", total).equals(15) yields "Expected spreadsheet total 12 to equal 15"
*/
@@ -41,15 +41,15 @@ Jasmine style assert mechanism, so we can have fancy english text for failures
/**
To implement a suite of unit tests, create a subkind of enyo.TestSuite.
Any methods in your subkind that begin with 'test' will be invoked as unit tests when the test runner executes.
-
- When each test is complete, it should call this.finish().
+
+ When each test is complete, it should call this.finish().
Pass nothing for success, or something truthy for failure (usually an explanatory message or an exception object).
- If you do not call finish(), your test will be failed after a 3-second timeout.
+ If you do not call finish(), your test will be failed after a 3-second timeout.
This timeout can be customized for a given test by calling this.resetTimeout(ms).
-
-
+
+
See enyo-support/tests for example framework tests.
-
+
*/
enyo.kind({
name: "enyo.TestSuite",
@@ -62,14 +62,14 @@ enyo.kind({
timeout: 3000,
timeoutMessage: "timed out",
/** @public
- Replaces the current test timeout with
+ Replaces the current test timeout with
May be called by individual tests to reset/lengthen/shorten the test timeout.
- Mostly good for unusually long-running tests, but can be used for shortening the timeout duration, or
+ Mostly good for unusually long-running tests, but can be used for shortening the timeout duration, or
even for setting different timeouts for successive stages of a test.
*/
resetTimeout: function(timeout) {
this.clearTimer();
- this.timer = window.setTimeout(enyo.bind(this, "timedout"), timeout || this.timeout);
+ this.timer = window.setTimeout(this.bindSafely("timedout"), timeout || this.timeout);
},
/** @public
Tests can call this.log() to print useful diagnostic information.
@@ -160,7 +160,7 @@ enyo.kind({
enyo.asyncMethod(this, "reallyFinish", inMessage);
},
reallyFinish: function(inMessage) {
- // If finish has been called before, then we ignore it
+ // If finish has been called before, then we ignore it
// unless we passed previously and now we're failing.
// We will send multiple finish events if we get a success and then a failure -- that counts as a failure.
if (this.results) {
@@ -219,7 +219,7 @@ enyo.kind({
enyo.asyncMethod(this, "next");
}
}
-
+
});
enyo.TestSuite.tests = [];
View
8 tools/test/core/tests/ComponentTest.js
@@ -26,14 +26,14 @@ enyo.kind({
this.finish();
},
testStartJob: function() {
- var finish = enyo.bind(this, "finish");
+ var finish = this.bindSafely("finish");
var c = new enyo.Component();
c.startJob("testStartJob", function() {
finish();
}, 10);
},
testStartJobStringName: function() {
- var finish = enyo.bind(this, "finish");
+ var finish = this.bindSafely("finish");
var c = new enyo.Component({
pass: function() {
finish();
@@ -42,7 +42,7 @@ enyo.kind({
c.startJob("testStartJobStringName", "pass", 10);
},
testStopJob: function() {
- var finish = enyo.bind(this, "finish");
+ var finish = this.bindSafely("finish");
var c = new enyo.Component();
c.startJob("testStopJob", function() {
finish("job wasn't stopped");
@@ -53,7 +53,7 @@ enyo.kind({
}, 30);
},
testDestroyJob: function() {
- var finish = enyo.bind(this, "finish");
+ var finish = this.bindSafely("finish");
var c = new enyo.Component();
c.startJob("testDestroyJob", function() {
finish("job wasn't stopped on destroy");
View
10 tools/test/core/tests/LoaderTest.js
@@ -2,8 +2,8 @@ enyo.kind({
name: "LoaderTest",
kind: enyo.TestSuite,
testSingleLoad: function() {
- enyo.load("tests/loader/loader1.js", enyo.bind(this,
- function() {
+ enyo.load("tests/loader/loader1.js",
+ this.bindSafely(function() {
if (window.LOADER_TEST === "loader1") {
this.finish();
}
@@ -15,7 +15,7 @@ enyo.kind({
},
testMultipleLoad: function() {
enyo.load(["tests/loader/loader2a.js", "tests/loader/loader2b.js"],
- enyo.bind(this, function() {
+ this.bindSafely(function() {
if (window.LOADER_TEST === "loader2b") {
this.finish();
}
@@ -27,7 +27,7 @@ enyo.kind({
},
testMultipleLoadWith404: function() {
enyo.load(["tests/loader/loader2b.js", "tests/loader/loader2a.js", "tests/loader/anotherfilethatdoesnotexist.js"],
- enyo.bind(this, function(block) {
+ this.bindSafely(function(block) {
if (window.LOADER_TEST === "loader2a" && block.failed.length === 1 && block.failed[0] === "./tests/loader/anotherfilethatdoesnotexist.js") {
this.finish();
}
@@ -40,7 +40,7 @@ enyo.kind({
testPackageLoad: function() {
// added a new folder (loader) with a package.js looking for a file setting window.PACKAGE_TEST and a file that doesn't exist
enyo.load(["tests/loader"],
- enyo.bind(this, function(block) {
+ this.bindSafely(function(block) {
if (window.PACKAGE_TEST === "loaded" && block.failed.length === 1 && block.failed[0] === "./tests/loader/nothere.js") {
this.finish();
}
Please sign in to comment.
Something went wrong with that request. Please try again.