Super Class Property Undefined Despite Super Call - javascript

I'm attempting to program a text adventure game using ECMAScript 6, transpiling into ECMAScript 5 using Babel. I've run into a strange case that's not appearing elsewhere in my code.
I have a generic action class that parses commands, determines if a given input matches a command that triggers the action, and then calls function to actually execute the action.
/**
* An action represents a mapping between a string and some kind of action in
* the system.
*/
export class Action {
/**
* Construct a new action instance.
*/
constuctor () {
this.commands = [];
}
/**
* Add a command as a trigger for this action.
*
* #param {String} command The command string to add as a trigger.
*
* #returns {Boolean} True if the command was added, false otherwise.
*/
addCommand (command) {
var paramRegex = /\{([a-zA-Z0-9_]+)\}/g;
var i = 0;
var regexResult;
var commandRegex;
var parameters;
if (typeof command !== 'string' || command.length < 1) {
return false;
}
parameters = {};
// Make spaces generic
commandRegex = command.replace(/\w+/g, '\\w+');
regexResult = paramRegex.exec(commandRegex);
while (regexResult) {
parameters[i] = regexResult[1];
commandRegex = commandRegex.replace(regexResult[0], '([^\\w]+)');
i++;
regexResult = paramRegex.exec(commandRegex);
}
this.commands.push({
regex: new RegExp(commandRegex, 'gi'),
parameters: parameters
});
return true;
}
/**
* Determine if the action has a trigger matching the given command string.
*
* #param {String} command The command to parse.
*
* #returns {Boolean} True if the command triggers the action, false
* otherwise.
*/
isTrigger (command) {
var i;
for (i = 0; i < this.commands.length; i++) {
if (this.commands[i].regex.test(command)) {
return true;
}
}
return false;
}
/**
* Trigger the action using the given command. This method checks if the
* command is a trigger for the action, extracts any arguments from the
* command string, and passes them to the {#link Action#execute} function
* using {#link Function#apply}.
*
* #param {String} command The command to use to trigger the action.
*
* #returns {Boolean} True if the trigger was successful, false otherwise.
*/
trigger (command) {
var args;
var result;
var i;
for (i = 0; i < this.commands.length; i++) {
result = this.commands[i].regex.exec(command);
if (result != null) {
break;
}
}
if (result == null) {
return false;
}
args = [];
for (i = 1; i < result.length; i++) {
args.push(result[i]);
}
this.execute.apply(this, args);
}
/**
* Execute the action with the given arguments.
*
* #returns {Boolean} True if the execution was successful, false otherwise.
*
* #virtual
*/
execute () {
throw new Error('Not implemented');
}
}
I have a sub-class which is supposed to implement a movement action and move the player from one location to another.
/**
* An action that moves the player.
*
* #extends {Action}
*/
export class MoveAction extends Action {
constructor () {
super();
this.addCommand('go to {place}');
this.addCommand('move to {place}');
this.addCommand('go {place}');
}
execute (place) {
var loc = GameObject.getGameObject(place);
if (!loc) {
return false;
}
return player.setLocation(loc);
}
}
I've run Babel on my code to produce compatible ECMAScript 5 code and attempted to run them manually via Node.JS to validate they work before I push them to my Git repository (I plan to write the test cases later).
I've run into an issue, though, where Node.JS complains that this.commands is undefined.
> var MoveAction = require('./compiled/actions/moveAction').MoveAction;
undefined
> var act = new MoveAction();
TypeError: Cannot read property 'push' of undefined
at MoveAction.addCommand (compiled\action.js:64:26)
at new MoveAction (compiled\actions\moveAction.js:41:39)
at repl:1:11
at REPLServer.defaultEval (repl.js:248:27)
at bound (domain.js:280:14)
at REPLServer.runBound [as eval] (domain.js:293:12)
at REPLServer.<anonymous> (repl.js:412:12)
at emitOne (events.js:82:20)
at REPLServer.emit (events.js:169:7)
at REPLServer.Interface._onLine (readline.js:210:10)
I can't spot anything immediately wrong with what I've written and I've done something similar in other places in the current code without any issues. I'm going to keep poking at it to see if I can solve the issue myself, but if anyone has suggestions that could save me time, it'd be much appreciated.

Solved it, I misspelled constructor as constuctor.

Related

How to create a function with return types in TypeScript?

I'm using Aurelia and TypeScript for my web application.
I created following 'user_checking' function to check users and status as follows.
user_checking(registerdata, tested_users) {
return registerdata.userid && typeof tested_users[registerdata.userid] == 'undefined';
}
This function has two parameters and also a return type. I don't see any kind of error there. But when I run the application, I'm getting following error.
error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number' or an enum type. [07:46:58] gulp-notify: [Error running Gulp] Error:
Can anyone please tell me what's the error in this function.
Actually, as was also mentioned in the comments, your code snippet works fine when compiled with TypeScript v2.3.x.
You first might want to check your TypeScript version through tsc --v and update it if required.
I've taken the snippet from GitLab you mentioned and tested that it works as expected. The two basic examples below arrange and return both the 'true' and 'false':
/**
* Example that arranges the function to return 'true'
*/
public exampleTrue(): boolean {
let registerdata = { userid: 1001 };
let tested_users = [];
return this.register.user_checking(registerdata, tested_users);
}
/**
* Example that arranges the function to return 'false'
*/
public exampleFalse(): boolean {
let registerdata = { userid: 1001 };
let tested_users = [];
this.tested_users[registerdata.userid] = 'foo';
return this.register.user_checking(registerdata, this.tested_users);
}
For the record, here is your class without any modifications:
import {autoinject} from 'aurelia-framework';
import {HttpClient, json} from 'aurelia-fetch-client';
#autoinject
export class Register {
heading:string = 'sTeam register';
registerdata = {};
passwordmatch = true;
tested_users = {}; //in coffee this is 'tested_users = {}'
/**
* CoffeeScript(AngularJS based) code as follows.
* ------------------------------
* S.tested_users = () ->
* tested_users
* ------------------------------
* #param tested_users
* #returns {any}
*/
tested_users_check(tested_users) {
return tested_users;
}
/**
* CoffeeScript(AngularJS based) code as follows.
* ------------------------------
* S.user_checking = ->
* S.registerdata.userid and typeof tested_users[S.registerdata.userid] == 'undefined'
* ------------------------------
* #param registerdata
* #param tested_users
* #returns {number}
*/
user_checking(registerdata, tested_users) {
return registerdata.userid && typeof tested_users[registerdata.userid] == 'undefined';
}
/**
* CoffeeScript(AngularJS based) code as follows.
* ------------------------------
* S.user_available = ->
* typeof tested_users[S.registerdata.userid] != 'undefined' and !tested_users[S.registerdata.userid]
* ------------------------------
* #param registerdata
* #param tested_users
*/
user_available(registerdata, tested_users) {
typeof tested_users[registerdata.userid] != 'undefined' && !tested_users[registerdata.userid];
}
/**
* CoffeeScript(AngularJS based) code as follows.
* ------------------------------
* S.user_taken = ->
* typeof tested_users[S.registerdata.userid] != 'undefined' and tested_users[S.registerdata.userid]
* ------------------------------
* #param registerdata
* #param tested_users
*/
user_taken(registerdata, tested_users) {
typeof tested_users[registerdata.userid] != 'undefined' && tested_users[registerdata.userid];
}
/**
* CoffeeScript(AngularJS based) code as follows.
* ------------------------------
* S.register = ->
* S.registerdata.group = 'techgrind'
* steam.post('register', S.registerdata).then(handle_request)
* ------------------------------
* #param registerdata
*/
register(registerdata) {
registerdata.group = 'kaishr';
// Have to implement the steam post method call.
}
}
As a sidenote I want to point out that it might be seriously helpful to take advantage of the strong-typing of TypeScript. Your class hardly uses any of the constrains nor type declarations. But perhaps, as a starting point, you might appreciate some minor tweaking like:
// define a strong-typed array, constrained to numbers
// (assuming userid is a number)
tested_users: Array<number> = [];
// define the class 'RegisterData'
// forcing it to have a userid of type number
export class RegisterData {
public userid: number;
}
With this in place you can refactor your operation a bit more to take full advantage of the compile-time safety and checks, before you even run your application. Like so:
public user_checking(registerdata: RegisterData, tested_users: Array<number>): boolean {
return registerdata.userid && typeof tested_users[registerdata.userid] == 'undefined';
}
Don't be overwhelmed with what, at first sight, might look like a lot of overhead in ceremony. With only adding some types, you've done the following:
Forced your function to return boolean
Strong-typed the RegisterData class
Eliminated any possible type mismatches for userid
Constrained the input parameters to strong types
In the end, this will allow your compiler to help you feed much better info before you even run your program.
Hope this helped...

How do you expose a global javascript function without ESLint no-unused-var error?

The following code is valid in ESLint with Google's style guide with one exception; the closure function Counter gets a no-unused-vars error when the script is checked using ESLint.
/**
* Create a counter that is incremented and returned when called
* #return {object} - incrementor function
*/
function Counter() {
var _i = 0;
/**
* increment counter
* #return {int} - The incremented integer
*/
function _incrementor() {
_i++;
return _i;
}
_incrementor.incr = function() {
this.call();
return _incrementor;
};
_incrementor.val = function(val) {
if (!arguments.length) { return _i; }
_i = val;
return _incrementor;
};
return _incrementor;
}
I would like to have this function (or one structure the same way) as a standalone script that I can include in my HTML and then call from a different script like so:
var count = Counter()
.val(5);
count.incr()
console.log(count.val()) // prints => 6
I have tried including /* exported Counter */ at the top of the script but the error persists. How do I silence/fix this error?
Explicitly adding Counter to the global scope silences this error and prevents errors that may occur as a result of the implicit global scope used in the question.
/**
* Create a counter that is incremented and returned when called
* #return {object} - incrementor function
*/
this.Counter = function() {
var _i = 0;
/**
* increment counter
* #return {int} - The incremented integer
*/
function _incrementor() {
_i++;
return _i;
}
_incrementor.incr = function() {
this.call();
return _incrementor;
};
_incrementor.val = function(val) {
if (!arguments.length) {
return _i;
}
_i = val;
return _incrementor;
};
return _incrementor;
};
Here's some options for telling the linter to allow a global Counter variable:
Option #1: Add this comment to the top of the js file when you need to use the global variable:
/* globals Counter */
Option #2: Add the variable name to the globals property in your eslint configuration file:
// eslintrc.js
module.exports = {
// ...
globals: {
'Counter': true
}
}
See ESLint documentation here for more info.
Note: you can also use the env property in your config file for predefined sets of globals like: browser (ie. localStorage), jquery, node, etc.). See here.

Opening/Closing the modal window triggers window resize

In my angular app, i use modal windows for filters/selectors and there are many components which are binded with window resize event like SVG, wordcloud etc.. Whenever i open or close the modal window, it triggers window resize event which results in reloading of all those components (Charts)..
Is there a way to stop the triggering of window resize event while opening or closing the modal window..?.. I googled and got a thread about this issue which is still open..
https://github.com/driftyco/ionic/issues/2309
The window.resize is triggered intentionally by the framework itself -- from the source: ionic.trigger('resize');.
solution
option 1
You can just comment out the above mentioned line and create your own bundle of the ionic framework.
option 2
You can use decorator to replace $ionicModal service altogether by copying and modifying the source code. Let me know if anything unclear. I personally prefer this option as it should be easier to maintain in the long run and let you customise the behaviour of the modal even further.
HERE is an example of what I mean:
.config(function($provide){
$provide.decorator('$ionicModal',function($rootScope, $ionicBody, $compile, $timeout, $ionicPlatform, $ionicTemplateLoader, $q, $log) {
var PLATFORM_BACK_BUTTON_PRIORITY_MODAL = 200;
var delegate = (function(){
/**
* #ngdoc controller
* #name ionicModal
* #module ionic
* #description
* Instantiated by the {#link ionic.service:$ionicModal} service.
*
* Be sure to call [remove()](#remove) when you are done with each modal
* to clean it up and avoid memory leaks.
*
* Note: a modal will broadcast 'modal.shown', 'modal.hidden', and 'modal.removed' events from its originating
* scope, passing in itself as an event argument. Note: both modal.removed and modal.hidden are
* called when the modal is removed.
*/
var ModalView = ionic.views.Modal.inherit({
/**
* #ngdoc method
* #name ionicModal#initialize
* #description Creates a new modal controller instance.
* #param {object} options An options object with the following properties:
* - `{object=}` `scope` The scope to be a child of.
* Default: creates a child of $rootScope.
* - `{string=}` `animation` The animation to show & hide with.
* Default: 'slide-in-up'
* - `{boolean=}` `focusFirstInput` Whether to autofocus the first input of
* the modal when shown. Default: false.
* - `{boolean=}` `backdropClickToClose` Whether to close the modal on clicking the backdrop.
* Default: true.
* - `{boolean=}` `hardwareBackButtonClose` Whether the modal can be closed using the hardware
* back button on Android and similar devices. Default: true.
*/
initialize: function(opts) {
ionic.views.Modal.prototype.initialize.call(this, opts);
this.animation = opts.animation || 'slide-in-up';
},
/**
* #ngdoc method
* #name ionicModal#show
* #description Show this modal instance.
* #returns {promise} A promise which is resolved when the modal is finished animating in.
*/
show: function(target) {
var self = this;
if (self.scope.$$destroyed) {
$log.error('Cannot call ' + self.viewType + '.show() after remove(). Please create a new ' + self.viewType + ' instance.');
return;
}
var modalEl = angular.element(self.modalEl);
self.el.classList.remove('hide');
$timeout(function() {
$ionicBody.addClass(self.viewType + '-open');
}, 400);
if (!self.el.parentElement) {
modalEl.addClass(self.animation);
$ionicBody.append(self.el);
}
if (target && self.positionView) {
self.positionView(target, modalEl);
// set up a listener for in case the window size changes
ionic.on('resize',function() {
ionic.off('resize',null,window);
self.positionView(target,modalEl);
},window);
}
modalEl.addClass('ng-enter active')
.removeClass('ng-leave ng-leave-active');
self._isShown = true;
self._deregisterBackButton = $ionicPlatform.registerBackButtonAction(
self.hardwareBackButtonClose ? angular.bind(self, self.hide) : angular.noop,
PLATFORM_BACK_BUTTON_PRIORITY_MODAL
);
self._isOpenPromise = $q.defer();
ionic.views.Modal.prototype.show.call(self);
$timeout(function() {
modalEl.addClass('ng-enter-active');
// ionic.trigger('resize');
self.scope.$parent && self.scope.$parent.$broadcast(self.viewType + '.shown', self);
self.el.classList.add('active');
self.scope.$broadcast('$ionicHeader.align');
}, 20);
return $timeout(function() {
//After animating in, allow hide on backdrop click
self.$el.on('click', function(e) {
if (self.backdropClickToClose && e.target === self.el) {
self.hide();
}
});
}, 400);
},
/**
* #ngdoc method
* #name ionicModal#hide
* #description Hide this modal instance.
* #returns {promise} A promise which is resolved when the modal is finished animating out.
*/
hide: function() {
var self = this;
var modalEl = angular.element(self.modalEl);
self.el.classList.remove('active');
modalEl.addClass('ng-leave');
$timeout(function() {
modalEl.addClass('ng-leave-active')
.removeClass('ng-enter ng-enter-active active');
}, 20);
self.$el.off('click');
self._isShown = false;
self.scope.$parent && self.scope.$parent.$broadcast(self.viewType + '.hidden', self);
self._deregisterBackButton && self._deregisterBackButton();
ionic.views.Modal.prototype.hide.call(self);
// clean up event listeners
if (self.positionView) {
ionic.off('resize',null,window);
}
return $timeout(function() {
$ionicBody.removeClass(self.viewType + '-open');
self.el.classList.add('hide');
}, self.hideDelay || 320);
},
/**
* #ngdoc method
* #name ionicModal#remove
* #description Remove this modal instance from the DOM and clean up.
* #returns {promise} A promise which is resolved when the modal is finished animating out.
*/
remove: function() {
var self = this;
self.scope.$parent && self.scope.$parent.$broadcast(self.viewType + '.removed', self);
return self.hide().then(function() {
self.scope.$destroy();
self.$el.remove();
});
},
/**
* #ngdoc method
* #name ionicModal#isShown
* #returns boolean Whether this modal is currently shown.
*/
isShown: function() {
return !!this._isShown;
}
});
var createModal = function(templateString, options) {
// Create a new scope for the modal
var scope = options.scope && options.scope.$new() || $rootScope.$new(true);
options.viewType = options.viewType || 'modal';
angular.extend(scope, {
$hasHeader: false,
$hasSubheader: false,
$hasFooter: false,
$hasSubfooter: false,
$hasTabs: false,
$hasTabsTop: false
});
// Compile the template
var element = $compile('<ion-' + options.viewType + '>' + templateString + '</ion-' + options.viewType + '>')(scope);
options.$el = element;
options.el = element[0];
options.modalEl = options.el.querySelector('.' + options.viewType);
var modal = new ModalView(options);
modal.scope = scope;
// If this wasn't a defined scope, we can assign the viewType to the isolated scope
// we created
if (!options.scope) {
scope[ options.viewType ] = modal;
}
return modal;
};
return {
/**
* #ngdoc method
* #name $ionicModal#fromTemplate
* #param {string} templateString The template string to use as the modal's
* content.
* #param {object} options Options to be passed {#link ionic.controller:ionicModal#initialize ionicModal#initialize} method.
* #returns {object} An instance of an {#link ionic.controller:ionicModal}
* controller.
*/
fromTemplate: function(templateString, options) {
var modal = createModal(templateString, options || {});
return modal;
},
/**
* #ngdoc method
* #name $ionicModal#fromTemplateUrl
* #param {string} templateUrl The url to load the template from.
* #param {object} options Options to be passed {#link ionic.controller:ionicModal#initialize ionicModal#initialize} method.
* options object.
* #returns {promise} A promise that will be resolved with an instance of
* an {#link ionic.controller:ionicModal} controller.
*/
fromTemplateUrl: function(url, options, _) {
var cb;
//Deprecated: allow a callback as second parameter. Now we return a promise.
if (angular.isFunction(options)) {
cb = options;
options = _;
}
return $ionicTemplateLoader.load(url).then(function(templateString) {
var modal = createModal(templateString, options || {});
cb && cb(modal);
return modal;
});
}
}
})();
console.log(delegate);
return delegate;
});
})
caution
Changing the behaviour may have some unwanted side effects, but I do not know ionic framework that much. Moreover, if there are any negative side effects then surely that is a bad design of the framework itself, as general events should not be used for framework specific purposes. If I had a bit more time I will try to make a PR to have triggering of the event optional.

Event listener on non-DOM elements

I'm trying to implement a little MVC framework and now I'm implementing the view-model binder, I mean, when the model changes, trigger a refresh/render/whatever on the model. So, I need an event listener on a object:
model.on("customEvent",appendItem);
$("#button").on("click",function(){
model.add(item);
model.trigger("customEvent");
});
function appendItem(item) {
$("#content").append(item.toHTML());
}
So how can I create my event listener on objects?
If you are already using jQuery, you can use its built-in event handling facilities.
It's a little known fact that you can also put any kind of Javascript objects into a jQuery collection, not just DOM elements. Then you can use a limited set of jQuery methods (.data(), .prop(), .on(), .off(), .trigger() and .triggerHandler()) on these objects.
//create the model - it can be any kind of object
var model = {};
//pass it to the jQuery function and you have a jQuery collection
//you can put an event handler on the object
$(model).on('customEvent', function () { console.log('hehe'); });
//and trigger it
$(model).trigger('customEvent');
Read more in the Manual
Try the Demo
For those who are not using jQuery and are interested in wiring their own stuf, here's how you may achieve similar aim:
/**
* EventfulObject constructor/base.
* #type EventfulObject_L7.EventfulObjectConstructor|Function
*/
var EventfulObject = function() {
/**
* Map from event name to a list of subscribers.
* #type Object
*/
var event = {};
/**
* List of all instances of the EventfulObject type.
* #type Array
*/
var instances = [];
/**
* #returns {EventfulObject_L1.EventfulObjectConstructor} An `EventfulObject`.
*/
var EventfulObjectConstructor = function() {
instances.push(this);
};
EventfulObjectConstructor.prototype = {
/**
* Broadcasts an event of the given name.
* All instances that wish to receive a broadcast must implement the `receiveBroadcast` method, the event that is being broadcast will be passed to the implementation.
* #param {String} name Event name.
* #returns {undefined}
*/
broadcast: function(name) {
instances.forEach(function(instance) {
(instance.hasOwnProperty("receiveBroadcast") && typeof instance["receiveBroadcast"] === "function") &&
instance["receiveBroadcast"](name);
});
},
/**
* Emits an event of the given name only to instances that are subscribed to it.
* #param {String} name Event name.
* #returns {undefined}
*/
emit: function(name) {
event.hasOwnProperty(name) && event[name].forEach(function(subscription) {
subscription.process.call(subscription.context);
});
},
/**
* Registers the given action as a listener to the named event.
* This method will first create an event identified by the given name if one does not exist already.
* #param {String} name Event name.
* #param {Function} action Listener.
* #returns {Function} A deregistration function for this listener.
*/
on: function(name, action) {
event.hasOwnProperty(name) || (event[name] = []);
event[name].push({
context: this,
process: action
});
var subscriptionIndex = event[name].length - 1;
return function() {
event[name].splice(subscriptionIndex, 1);
};
}
};
return EventfulObjectConstructor;
}();
var Model = function(id) {
EventfulObject.call(this);
this.id = id;
this.receiveBroadcast = function(name) {
console.log("I smell another " + name + "; and I'm model " + this.id);
};
};
Model.prototype = Object.create(EventfulObject.prototype);
Model.prototype.constructor = Model;
// ---------- TEST AND USAGE (hopefully it's clear enough...)
// ---------- note: I'm not testing event deregistration.
var ob1 = new EventfulObject();
ob1.on("crap", function() {
console.log("Speaking about craps on a broadcast? - Count me out!");
});
var model1 = new Model(1);
var model2 = new Model(2);
model2.on("bust", function() {
console.log("I'm model2 and I'm busting!");
});
var ob2 = new EventfulObject();
ob2.on("bust", function() {
console.log("I'm ob2 - busted!!!");
});
ob2.receiveBroadcast = function() {
console.log("If it zips, I'll catch it. - That's me ob2.");
};
console.log("start:BROADCAST\n---------------");
model1.broadcast("crap");
console.log("end :BROADCAST\n---------------\n-\n-\n");
console.log("start:EMIT\n---------------");
ob1.emit("bust");
console.log("end:EMIT\n---------------");
<h1>THE CODE IS IN THE JavaScript SECTION!</h1>
<h3>AND... THE SHOW IS ON YOUR CONSOLE!</h3>

apply proxy function to eventhandler using Hammer library

The proxy function will not work, but a normal function is ok
What can i do to make it work
It's problably a scope issue, but how do I use "apply" or "call" in this context?
delegateEvents: function(){
for (var key in this.events) {
var methodName = this.events[key];
var method = this.proxy(this[methodName]);
var match = key.match(this.eventSplitter);
var eventName = match[1], selector = match[2];
if (selector === '') {
this.el.bind(eventName, method);
} else {
eventName == "click" && ( eventName = "tap click");
console.log("selector",selector);
console.log("eventName",eventName);
var eel = $$(selector, $(this.el));
Hammer(eel).on(eventName, method);
}
}
}
});
and a part of Hammer
Hammer.Instance.prototype = {
/**
* bind events to the instance
* #param {String} gesture
* #param {Function} handler
* #returns {Hammer.Instance}
*/
on: function onEvent(gesture, handler){
var gestures = gesture.split(' ');
for(var t=0; t<gestures.length; t++) {
this.element.addEventListener(gestures[t], handler, false);
}
return this;
},
proxy function
util = {
proxy: function(fn, context, args){
var argsArr = slice(arguments, 2); //curried args
return function(){
return fn.apply(context, argsArr.concat(slice(arguments)));
};
},
becomes part of the controller
result.proxy = function(func){ return util.proxy(func, this); };
so, this.proxy == result.proxy and the context is already set to this
thanks if anyone knows this
The problem was that the Minified library returned a selector that the Hammer library
could not work with.
Well, that was my first assumption
It was actually the fact that I was using one type of selector for two different functions, the "$" version, wich returns the Minified object, but Hammer just needs the node and therefore needs the "$$" version.

Categories