> handsontable.full.js?v=1.2:10476 Uncaught TypeError: Cannot read property 'classList' of undefined
> at _removeClass (handsontable.full.js?v=1.2:10476)
> at removeClass (handsontable.full.js?v=1.2:10531)
> at handsontable.full.js?v=1.2:5426
> at done (handsontable.full.js?v=1.2:5502)
> at handsontable.full.js?v=1.2:5517
> at handsontable.full.js?v=1.2:23966
> at ColumnSettings.Handsontable.AutocompleteValidator [as validator] (handsontable.full.js?v=1.2:23949)
It's working fine when i am pasting 4 to 5 columns but when i trying more than 7 getting above error.previously i tested in tab it's works fine there but when i moved handsontable grid to bootstrap modal getting this error.please help i am trying to understand.
please find below how i created handsontable
manualHot = new Handsontable(container, {
rowHeaders: true,
colHeaders: true,
startRows: 100,
startCols: 125,
// columns: columns,
dropdownMenu: true,
contextMenu: true,
filters:true,
manualColumnResize: true,
height: 500,
wordWrap:false,
colWidths: 100,
rowHeights: 23,
autoColumnSize: true,
columnSorting: true,
fixedRowsTop:1,
copyable:true,
sortFunction: function(sortOrder, columnMeta) {
return function(a, b) {
var plugin = manualHot.getPlugin('columnSorting');
var sortFunction;
if (a[0] === 0) {
return -1;
}
switch (columnMeta.type) {
case 'date':
sortFunction = plugin.dateSort;
break;
case 'numeric':
sortFunction = plugin.numericSort;
break;
default:
sortFunction = plugin.defaultSort;
}
return sortFunction(sortOrder, columnMeta)(a, b);
};
},
trimDropdown :false,
formulas: true,
cells: function (row, col, prop) {
var cellProperties = {};
if (row === 0) {
cellProperties.type = 'dropdown';
cellProperties.source= colHeaderArr;
cellProperties.allowInvalid = false;
}
return cellProperties;
},
afterValidate: function (isValid, value, row, prop, source) {
if (source === 'loadData' || source === 'internal' ) {
return;
}
if (!isValid ) {
errorHeader[prop]=headerCols[prop];
}
},
});
manualHot.addHook('beforeChange', function(changes, source) {
dupheaders=[]
dupheaders.length=0;
if (source === 'loadData' || source === 'internal' || changes.length > 1) {
return;
}
var row = changes[0][0];
var prop = changes[0][1];
var newVal = changes[0][3];
var oldVal=changes[0][2];
if ( row ===0) {
headerCols[prop]=newVal;
var sorted_head = headerCols.slice().sort();
for (var i = 0; i < headerCols.length - 1; i++) {
if (sorted_head[i + 1] == sorted_head[i]) {
if(sorted_head[i]){dupheaders.push(sorted_head[i]);}
}
}
if(dupheaders.length>0){
var errMsg='Duplicate Headers :<br>'+dupheaders;
setTimeout(function(){$('#manualDataOnlinePrep-Tab').unblock();},1000);
common.showMessage(errMsg, false, "error", "middle-center");
}
errorHeader.splice(prop, 1);
setColumnType(colProp,prop,newVal, this,txnTypeArr);
}
});
manualHot.init();
}
function setColumnType(columnProp,col,newVal, instance,ddValue) {
if(newVal === 'Reporting Code Description' ) {
columnProp[col].type='dropdown';
columnProp[col].source=ddValue;
}
else{
if(newVal.match(/^.*Date$/)){
columnProp[col].type='date';
columnProp[col].dateFormat=document.getElementById("dateFormat_2").value;
columnProp[col].allowInvalid=true;
}
else if (newVal.match(/^.*Amount$/)){
columnProp[col].type='numeric';
columnProp[col].format= '[00,00,000].00';
columnProp[col].allowInvalid=false;
}
else if (newVal.match(/^.*Rate$/)){
columnProp[col].type='numeric';
columnProp[col].format= '0.[000000]';
columnProp[col].allowInvalid=false;
}
else{
columnProp[col].type='text';
}
}
instance.updateSettings({columns: columnProp},{contextMenu:true});
instance.validateCells(function() {
instance.render();
});
}
Related
I am implementing a custom solution that will initialize kendoGrid (and its multiselect columns) by applying filters to the grid using query string parameters. I am using Kendo UI v2014.2.903 and multiselectbox user extension.
To achieve this, I have a custom JavaScript function that parses query string parameters into filter object and applies it to kendoGrid's dataSource property using its filter method. Code snippet below:
var preFilterQueryString = getPreFilterQuery(queryString);
if (preFilterQueryString != '') {
kendoGrid.dataSource.filter(parseFilterString(preFilterQueryString));
}
I am initializing multiselectbox as follows:
args.element.kendoMultiSelectBox({
dataSource: getAutoCompleteDataSource(column.field),
valuePrimitive: true,
selectionChanged: function (e) {
//ignored for brevity
}
});
My problem is if I set filters to multiselectbox using above approach, they are not applied correctly to the data set.
For example, if I pass single filter as "Vancouver", correct result set is displayed. Choices in the multiselectbox are "All" and "Vancouver". However, Vancouver choice is not checked in the multiselectbox. Is that by design? Please see image below.
single filter image
If I pass in two filters Vancouver and Warsaw, only the last filter Warsaw is applied to the grid and data set containing only Warsaw is displayed. Again, none of the choices are checked in the multiselectbox.
two filters image
Below is the filter object that is applied to dataSource.filter() method.
kendoGrid's filter object image
Troubleshooting
Below is condensed version (for brevity) of selectionchanged event handler of a multiselectbox column.
if (e.newValue && e.newValue.length) {
var newValue = e.newValue;
console.log('e.newValue: ' + e.newValue);
filter.filters = [];
for (var i = 0, l = newValue.length; i < l; i++) {
filter.filters.push({field: field,operator: 'contains',value: newValue[i]});
}
kendoGrid.dataSource.filter(allFilters);
}
I noticed that when two filters are passed, e.newValue is "Warsaw" instead of an array - "[Vancouver, Warsaw]"
I spent lot of time troubleshooting why only Warsaw is applied to the data set.
Here's what I found out:
_raiseSelectionChanged function is what raises selection changed event. In that function, I noticed that it sets newValue and oldValue event arguments. To set newValue it uses "Value" function. Value function uses the below code to retrieve all the selected list items and return them.
else {
var selecteditems = this._getSelectedListItems();
return $.map(selecteditems, function (item) {
var obj = $(item).children("span").first();
return obj.attr("data-value");
}).join();
}
_getSelectedListItems function uses jQuery filter to fetch all list items that have css class ".selected"
_getSelectedListItems: function () {
return this._getAllValueListItems().filter("." + SELECTED);
},
There's a _select event which seems to be adding ".selected" class to list items. When I put a breakpoint on line 286, it is hitting it only once and that is for Warsaw. I am unable to understand why it is not getting called for Vancouver. I am out of clues and was wondering if anyone has pointers.
debug capture of _select function
Below is kendoMultiSelectBox user extension for your reference:
//MultiSelect - A user extension of KendoUI DropDownList widget.
(function ($) {
// shorten references to variables
var kendo = window.kendo,
ui = kendo.ui,
DropDownList = ui.DropDownList,
keys = kendo.keys,
SELECT = "select",
SELECTIONCHANGED = "selectionChanged",
SELECTED = "k-state-selected",
HIGHLIGHTED = "k-state-active",
CHECKBOX = "custom-multiselect-check-item",
SELECTALLITEM = "custom-multiselect-selectAll-item",
MULTISELECTPOPUP = "custom-multiselect-popup",
EMPTYSELECTION = "custom-multiselect-summary-empty";
var lineTemplate = '<input type="checkbox" name="#= {1} #" value="#= {0} #" class="' + CHECKBOX + '" />' +
'<span data-value="#= {0} #">#= {1} #</span>';
var MultiSelectBox = DropDownList.extend({
init: function (element, options) {
options.template = kendo.template(kendo.format(lineTemplate, options.dataValueField || 'data', options.dataTextField || 'data'));
// base call to widget initialization
DropDownList.fn.init.call(this, element, options);
var button = $('<input type="button" value="OK" style="float: right; padding: 3px; margin: 5px; cursor: pointer;" />');
button.on('click', $.proxy(this.close, this));
var popup = $(this.popup.element);
popup.append(button);
},
options: {
name: "MultiSelectBox",
index: -1,
showSelectAll: true,
preSummaryCount: 1, // number of items to show before summarising
emptySelectionLabel: '', // what to show when no items are selected
selectionChanged: null // provide callback to invoke when selection has changed
},
events: [
SELECTIONCHANGED
],
refresh: function () {
// base call
DropDownList.fn.refresh.call(this);
this._updateSummary();
$(this.popup.element).addClass(MULTISELECTPOPUP);
},
current: function (candidate) {
return this._current;
},
open: function () {
var self = this;
this._removeSelectAllItem();
this._addSelectAllItem();
if ($(this.ul).find('li').length > 6) {
$(this.popup.element).css({ 'padding-bottom': '30px' });
}
else {
$(this.popup.element).css({ 'padding-bottom': '0' });
}
DropDownList.fn.open.call(this);
//hook on to popup event because dropdown close does not
//fire consistently when user clicks on some other elements
//like a dataviz chart graphic
this.popup.one('close', $.proxy(this._onPopupClosed, this));
},
_onPopupClosed: function () {
this._removeSelectAllItem();
this._current = null;
this._raiseSelectionChanged();
},
_raiseSelectionChanged: function () {
var currentValue = this.value();
var currentValues = $.map((currentValue.length > 0 ? currentValue.split(",") : []).sort(), function (item) { return item.toString(); });
var oldValues = $.map((this._oldValue || []).sort(), function (item) { return item ? item.toString() : ''; });
// store for next pass
this._oldValue = $.map(currentValues, function (item) { return item.toString(); });
var changedArgs = { newValue: currentValues, oldValue: oldValues };
if (oldValues) {
var hasChanged = ($(oldValues).not(currentValues).length == 0 && $(currentValues).not(oldValues).length == 0) !== true;
if (hasChanged) {
//if (this.options.selectionChanged)
// this.options.selectionChanged(changedArgs);
this.trigger(SELECTIONCHANGED, changedArgs);
}
}
else if (currentValue.length > 0) {
//if (this.options.selectionChanged)
// this.options.selectionChanged(changedArgs);
this.trigger(SELECTIONCHANGED, changedArgs);
}
},
_addSelectAllItem: function () {
if (!this.options.showSelectAll) return;
var firstListItem = this.ul.children('li:first');
if (firstListItem.length > 0) {
this.selectAllListItem = $('<li tabindex="-1" role="option" unselectable="on" class="k-item ' + SELECTALLITEM + '"></li>').insertBefore(firstListItem);
// fake a data object to use for the template binding below
var selectAllData = {};
selectAllData[this.options.dataValueField || 'data'] = '*';
selectAllData[this.options.dataTextField || 'data'] = 'All';
this.selectAllListItem.html(this.options.template(selectAllData));
this._updateSelectAllItem();
this._makeUnselectable(); // required for IE8
}
},
_removeSelectAllItem: function () {
if (this.selectAllListItem) {
this.selectAllListItem.remove();
}
this.selectAllListItem = null;
},
_focus: function (li) {
if (this.popup.visible() && li && this.trigger(SELECT, { item: li })) {
this.close();
return;
}
this.select(li);
},
_keydown: function (e) {
// currently ignore Home and End keys
// can be added later
if (e.keyCode == kendo.keys.HOME ||
e.keyCode == kendo.keys.END) {
e.preventDefault();
return;
}
DropDownList.fn._keydown.call(this, e);
},
_keypress: function (e) {
// disable existing function
},
_move: function (e) {
var that = this,
key = e.keyCode,
ul = that.ul[0],
down = key === keys.DOWN,
pressed;
if (key === keys.UP || down) {
if (down) {
if (!that.popup.visible()) {
that.toggle(down);
}
if (!that._current) {
that._current = ul.firstChild;
} else {
that._current = ($(that._current)[0].nextSibling || that._current);
}
} else {
//up
// only if anything is highlighted
if (that._current) {
that._current = ($(that._current)[0].previousSibling || ul.firstChild);
}
}
if (that._current) {
that._scroll(that._current);
}
that._highlightCurrent();
e.preventDefault();
pressed = true;
} else {
pressed = DropDownList.fn._move.call(this, e);
}
return pressed;
},
selectAll: function () {
var unselectedItems = this._getUnselectedListItems();
this._selectItems(unselectedItems);
// todo: raise custom event
},
unselectAll: function () {
var selectedItems = this._getSelectedListItems();
this._selectItems(selectedItems); // will invert the selection
// todo: raise custom event
},
_selectItems: function (listItems) {
var that = this;
$.each(listItems, function (i, item) {
var idx = ui.List.inArray(item, that.ul[0]);
that.select(idx); // select OR unselect
});
},
_selectItem: function () {
// method override to prevent default selection of first item, done by normal dropdown
var that = this,
options = that.options,
useOptionIndex,
value;
useOptionIndex = that._isSelect && !that._initial && !options.value && options.index && !that._bound;
if (!useOptionIndex) {
value = that._selectedValue || options.value || that._accessor();
}
if (value) {
that.value(value);
} else if (that._bound === undefined) {
that.select(options.index);
}
},
_select: function (li) {
var that = this,
value,
text,
idx;
li = that._get(li);
if (li && li[0]) {
idx = ui.List.inArray(li[0], that.ul[0]);
if (idx > -1) {
if (li.hasClass(SELECTED)) {
li.removeClass(SELECTED);
that._uncheckItem(li);
if (this.selectAllListItem && li[0] === this.selectAllListItem[0]) {
this.unselectAll();
}
} else {
li.addClass(SELECTED);
that._checkItem(li);
if (this.selectAllListItem && li[0] === this.selectAllListItem[0]) {
this.selectAll();
}
}
if (this._open) {
that._current(li);
that._highlightCurrent();
}
var selecteditems = this._getSelectedListItems();
value = [];
text = [];
$.each(selecteditems, function (indx, item) {
var obj = $(item).children("span").first();
value.push(obj.attr("data-value"));
text.push(obj.text());
});
that._updateSummary(text);
that._updateSelectAllItem();
that._accessor(value, idx);
// todo: raise change event (add support for selectedIndex) if required
that._raiseSelectionChanged();
}
}
},
_getAllValueListItems: function () {
if (this.selectAllListItem) {
return this.ul.children("li").not(this.selectAllListItem[0]);
} else {
return this.ul.children("li");
}
},
_getSelectedListItems: function () {
return this._getAllValueListItems().filter("." + SELECTED);
},
_getUnselectedListItems: function () {
return this._getAllValueListItems().filter(":not(." + SELECTED + ")");
},
_getSelectedItemsText: function () {
var text = [];
var selecteditems = this._getSelectedListItems();
$.each(selecteditems, function (indx, item) {
var obj = $(item).children("span").first();
text.push(obj.text());
});
return text;
},
_updateSelectAllItem: function () {
if (!this.selectAllListItem) return;
// are all items selected?
if (this._getAllValueListItems().length == this._getSelectedListItems().length) {
this._checkItem(this.selectAllListItem);
this.selectAllListItem.addClass(SELECTED);
}
else {
this._uncheckItem(this.selectAllListItem);
this.selectAllListItem.removeClass(SELECTED);
}
},
_updateSummary: function (itemsText) {
if (!itemsText) {
itemsText = this._getSelectedItemsText();
}
if (itemsText.length == 0) {
this._inputWrapper.addClass(EMPTYSELECTION);
this.text(this.options.emptySelectionLabel);
return;
} else {
this._inputWrapper.removeClass(EMPTYSELECTION);
}
if (itemsText.length <= this.options.preSummaryCount) {
this._textAccessor(itemsText.join(", "));
}
else {
this._textAccessor(itemsText.length + ' selected');
}
},
_checkItem: function (itemContainer) {
if (!itemContainer) return;
itemContainer.children("input").prop("checked", true);
},
_uncheckItem: function (itemContainer) {
if (!itemContainer) return;
itemContainer.children("input").removeAttr("checked");
},
_isItemChecked: function (itemContainer) {
return itemContainer.children("input:checked").length > 0;
},
value: function (value) {
if(value != undefined)
console.log("value", value);
var that = this,
idx,
valuesList = [];
if (value !== undefined) {
if (!$.isArray(value)) {
valuesList.push(value);
this._oldValue = valuesList; // to allow for selectionChanged event
}
else {
valuesList = value;
this._oldValue = value; // to allow for selectionChanged event
}
// clear all selections first
$(that.ul[0]).children("li").removeClass(SELECTED);
$("input", that.ul[0]).removeAttr("checked");
$.each(valuesList, function (indx, item) {
var hasValue;
if (item !== null) {
item = item.toString();
}
that._selectedValue = item;
hasValue = value || (that.options.optionLabel && !that.element[0].disabled && value === "");
if (hasValue && that._fetchItems(value)) {
return;
}
idx = that._index(item);
if (idx > -1) {
that.select(
that.options.showSelectAll ? idx + 1 : idx
);
}
});
that._updateSummary();
}
else {
var selecteditems = this._getSelectedListItems();
return $.map(selecteditems, function (item) {
var obj = $(item).children("span").first();
return obj.attr("data-value");
}).join();
}
},
});
ui.plugin(MultiSelectBox);
})(jQuery);
UPDATE
I came across documentation for multiselectbox's select event that clearly states that the select event is not fired when an item is selected programmatically. Is it relevant to what I am trying to do? But why is it firing for one of the filters even though I am setting them programmatically?
enter link description here
I have a directive which is depending on some input. I want to call that directive from angular controller when ever that input changed. Here is my directive.
var app = angular.module('App', ['ui.bootstrap']);
app.controller('fcController', function($scope, fcService, $uibModal) {
$scope.formatType = '1';
});
app.directive('fcsaNumber', function($filter) {
var addCommasToInteger, controlKeys, hasMultipleDecimals, isNotControlKey, isNotDigit, isNumber, makeIsValid, makeMaxDecimals, makeMaxDigits, makeMaxNumber, makeMinNumber;
isNumber = function(val) {
return !isNaN(parseFloat(val)) && isFinite(val);
};
isNotDigit = function(which) {
return which < 45 || which > 57 || which === 47;
};
controlKeys = [0, 8, 13];
isNotControlKey = function(which) {
return controlKeys.indexOf(which) === -1;
};
hasMultipleDecimals = function(val) {
return (val != null) && val.toString().split('.').length > 2;
};
makeMaxDecimals = function(maxDecimals) {
var regexString, validRegex;
if (maxDecimals > 0) {
regexString = "^-?\\d*\\.?\\d{0," + maxDecimals + "}$";
} else {
regexString = "^-?\\d*$";
}
validRegex = new RegExp(regexString);
return function(val) {
return validRegex.test(val);
};
};
makeMaxNumber = function(maxNumber) {
return function(val, number) {
return number <= maxNumber;
};
};
makeMinNumber = function(minNumber) {
return function(val, number) {
return number >= minNumber;
};
};
makeMaxDigits = function(maxDigits) {
var validRegex;
validRegex = new RegExp("^-?\\d{0," + maxDigits + "}(\\.\\d*)?$");
return function(val) {
return validRegex.test(val);
};
};
makeIsValid = function(options) {
var validations;
validations = [];
if (options.maxDecimals != null) {
validations.push(makeMaxDecimals(options.maxDecimals));
}
if (options.max != null) {
validations.push(makeMaxNumber(options.max));
}
if (options.min != null) {
validations.push(makeMinNumber(options.min));
}
if (options.maxDigits != null) {
validations.push(makeMaxDigits(options.maxDigits));
}
return function(val) {
var i, number, _i, _ref;
if (!isNumber(val)) {
return false;
}
if (hasMultipleDecimals(val)) {
return false;
}
number = Number(val);
for (i = _i = 0, _ref = validations.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
if (!validations[i](val, number)) {
return false;
}
}
return true;
};
};
addCommasToInteger = function(val) {
var commas, decimals, wholeNumbers;
decimals = val.indexOf('.') == -1 ? '.00' : val.replace(/^\d+(?=\.)/, '');
wholeNumbers = val.replace(/(\.\d+)$/, '');
commas = wholeNumbers.replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,');
return "" + commas + decimals.substring(0, 3);
};
return {
restrict: 'A',
require: 'ngModel',
scope: {
options: '#fcsaNumber',
},
link: function(scope, elem, attrs, ngModelCtrl) {
var isValid, options;
options = {};
if (scope.options != null) {
options = scope.$eval(scope.options);
}
isValid = makeIsValid(options);
ngModelCtrl.$parsers.unshift(function(viewVal) {
var noCommasVal;
noCommasVal = viewVal.replace(/,/g, '');
if (isValid(noCommasVal) || !noCommasVal) {
ngModelCtrl.$setValidity('fcsaNumber', true);
return noCommasVal;
} else {
ngModelCtrl.$setValidity('fcsaNumber', false);
return void 0;
}
});
ngModelCtrl.$formatters.push(function(val) {
if ((options.nullDisplay != null) && (!val || val === '')) {
return options.nullDisplay;
}
if ((val == null) || !isValid(val)) {
return val;
}
ngModelCtrl.$setValidity('fcsaNumber', true);
val = addCommasToInteger(val.toString());
if (options.key == 1) {
options.prepend = 'S/.';
}
if (options.key == 2) {
options.prepend = '$';
}
if (options.prepend != null) {
val = "" + options.prepend + val;
}
if (options.append != null) {
val = "" + val + options.append;
}
return val;
});
elem.on('blur', function() {
var formatter, viewValue, _i, _len, _ref;
viewValue = ngModelCtrl.$modelValue;
if ((viewValue == null) || !isValid(viewValue)) {
return;
}
_ref = ngModelCtrl.$formatters;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
formatter = _ref[_i];
viewValue = formatter(viewValue);
}
ngModelCtrl.$viewValue = viewValue;
return ngModelCtrl.$render();
});
elem.on('focus', function() {
var val;
val = elem.val();
if (options.prepend != null) {
val = val.replace(options.prepend, '');
}
if (options.append != null) {
val = val.replace(options.append, '');
}
elem.val(val.replace(/,/g, ''));
return elem[0].select();
});
if (options.preventInvalidInput === true) {
return elem.on('keypress', function(e) {
if (isNotDigit(e.which && isNotControlKey(e.which))) {
return e.preventDefault();
}
});
}
}
};
});
HTML:
<input type ="text" ng-model ="currency" fcsa-number="{key : {{formatType}}}">
Here $scope.formatType = '1'; is the input. If this formatType changed then That directive need to call. One friend suggested me this
link: function(scope,elem,attr,ctrl){
scope.$watch('fcsaNumber', function(){
// do your stuff
});
but it is not calling my directive manually.
even I have tried with
.controller("ctrl", function($scope) {
$scope.$watch('formatType', function() {
$scope.$broadcast("call_dir")
})
})
return {
restrict: 'A',
require: 'ngModel',
scope: {
options: '#fcsaNumber',
},
link: function(scope, elem, attrs, ngModelCtrl) {
var isValid, options;
options = {};
scope.$on('call_dir', function(ev) {
//your code
})
};
You need to watch the value that is going to change in your controller. In your case it is formatType.
You should try to use this expression in your isolated scope:
scope: {
options: '=fcsaNumber',
}
And if you want the event broadcasting to work, i.e. the $scope.$bradcast approach, then make sure your directive is a child of the controller scope in the scope hierarchy. Else a easier way (but not always recommended) would be to broadcast your event from $rootScope.
So something like this should also work (with your event based solution):
.controller("ctrl", function($scope, $rootScope) {
$scope.$watch('formatType', function() {
$rootScope.$broadcast("call_dir")
})
})
I need some help with my code. I want to make the str sprite invisible after 10 seconds.
I'm using this link for help: http://phaser.io/examples/v2/time/basic-timed-event
var game = new Phaser.Game(500, 550, Phaser.AUTO);
//var picture;
var Pacman = function (game) {
this.map = null;
this.layer = null;
this.pacman = null;
this.safetile = 14;
this.gridsize = 16;
this.speed = 100;
this.threshold = 3;
this.turnSpeed = 200;
this.marker = new Phaser.Point();
this.turnPoint = new Phaser.Point();
this.directions = [ null, null, null, null, null ];
this.opposites = [ Phaser.NONE, Phaser.RIGHT, Phaser.LEFT, Phaser.DOWN, Phaser.UP ];
this.current = Phaser.NONE;
this.turning = Phaser.NONE;
this.score=0;
this.scoreText='';
this.bonus=0;
this.bonusText='';
};
Pacman.prototype = {
init: function () {
this.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
this.scale.pageAlignHorizontally = true;
this.scale.pageAlignVertically = true;
Phaser.Canvas.setImageRenderingCrisp(this.game.canvas);
this.physics.startSystem(Phaser.Physics.ARCADE);
},
preload: function () {
// We need this because the assets are on github pages
// Remove the next 2 lines if running locally
this.load.baseURL = 'https://nikosdaskalos.github.io/pacman/';
this.load.crossOrigin = 'anonymous';
this.load.image('str', 'assets/str.png');
this.load.image('dot', 'assets/dot.jpg');
this.load.image('coin', 'assets/coin.jpg');
this.load.image('tiles', 'assets/pacman-tiles.png');
this.load.spritesheet('pacman', 'assets/pacman.png');
this.load.tilemap('map', 'assets/pacman-map.json', null, Phaser.Tilemap.TILED_JSON);
// Needless to say, graphics (C)opyright Namco
},
create: function () {
this.map = this.add.tilemap('map');
this.map.addTilesetImage('pacman-tiles', 'tiles');
this.layer = this.map.createLayer('Pacman');
//picture = game.add.sprite(game.world.centerX, game.world.centerY, 'str');
//picture.anchor.setTo(0.5, 0.5);
//game.time.events.add(Phaser.Timer.SECOND * 4, fadePicture, this);
this.dots = this.add.physicsGroup();
this.map.createFromTiles(36, this.safetile, 'str', this.layer, this.dots);
this.map.createFromTiles(7, this.safetile, 'dot', this.layer, this.dots);
this.map.createFromTiles(35, this.safetile, 'coin', this.layer, this.dots);
// The dots will need to be offset by 6px to put them back in the middle of the grid
this.dots.setAll('x', 6, false, false, 1);
this.dots.setAll('y', 6, false, false, 1);
// Pacman should collide with everything except the safe tile
this.map.setCollisionByExclusion([this.safetile], true, this.layer);
// Position Pacman at grid location 14x17 (the +8 accounts for his anchor)
this.pacman = this.add.sprite((14 * 16) + 8, (17 * 16) + 8, 'pacman', 0);
this.pacman.anchor.set(0.5);
//this.pacman.animations.add('munch', [0, 1, 2, 1], 20, true);
this.physics.arcade.enable(this.pacman);
this.pacman.body.setSize(16, 16, 0, 0);
this.cursors = this.input.keyboard.createCursorKeys();
//this.pacman.play('munch');
this.move(Phaser.LEFT);
this.scoreText = game.add.text(0, 500, 'Score: 0', { fontSize: '34px Arial', fill: 'white' });
lives = game.add.group();
game.add.text(game.world.width - 340, 500, 'Lives : ', { fontSize: '34px Arial', fill: 'white' });
this.bonusText = game.add.text(360, 500, 'Bonus: 0', { fontSize: '20px Arial', fill: 'white' });
for (var i = 0; i < 3; i++)
{
var pacman = lives.create(game.world.width - 235 + (30 * i), 517, 'pacman');
pacman.anchor.setTo(0.5, 0.5);
pacman.angle = 90;
pacman.alpha = 0.4;
}
},
//function fadePicture() {
//game.add.tween(picture).to( { alpha: 0 }, 2000, Phaser.Easing.Linear.None, true);
//},
checkKeys: function () {
if (this.cursors.left.isDown && this.current !== Phaser.LEFT)
{
this.checkDirection(Phaser.LEFT);
}
else if (this.cursors.right.isDown && this.current !== Phaser.RIGHT)
{
this.checkDirection(Phaser.RIGHT);
}
else if (this.cursors.up.isDown && this.current !== Phaser.UP)
{
this.checkDirection(Phaser.UP);
}
else if (this.cursors.down.isDown && this.current !== Phaser.DOWN)
{
this.checkDirection(Phaser.DOWN);
}
else
{
// This forces them to hold the key down to turn the corner
this.turning = Phaser.NONE;
}
},
checkDirection: function (turnTo) {
if (this.turning === turnTo || this.directions[turnTo] === null || this.directions[turnTo].index !== this.safetile)
{
// Invalid direction if they're already set to turn that way
// Or there is no tile there, or the tile isn't index 1 (a floor tile)
return;
}
// Check if they want to turn around and can
if (this.current === this.opposites[turnTo])
{
this.move(turnTo);
}
else
{
this.turning = turnTo;
this.turnPoint.x = (this.marker.x * this.gridsize) + (this.gridsize / 2);
this.turnPoint.y = (this.marker.y * this.gridsize) + (this.gridsize / 2);
}
},
turn: function () {
var cx = Math.floor(this.pacman.x);
var cy = Math.floor(this.pacman.y);
// This needs a threshold, because at high speeds you can't turn because the coordinates skip past
if (!this.math.fuzzyEqual(cx, this.turnPoint.x, this.threshold) || !this.math.fuzzyEqual(cy, this.turnPoint.y, this.threshold))
{
return false;
}
// Grid align before turning
this.pacman.x = this.turnPoint.x;
this.pacman.y = this.turnPoint.y;
this.pacman.body.reset(this.turnPoint.x, this.turnPoint.y);
this.move(this.turning);
this.turning = Phaser.NONE;
return true;
},
move: function (direction) {
var speed = this.speed;
if (direction === Phaser.LEFT || direction === Phaser.UP)
{
speed = -speed;
}
if (direction === Phaser.LEFT || direction === Phaser.RIGHT)
{
this.pacman.body.velocity.x = speed;
}
else
{
this.pacman.body.velocity.y = speed;
}
// Reset the scale and angle (Pacman is facing to the right in the sprite sheet)
/* this.pacman.scale.x = 1;
this.pacman.angle = 0;
if (direction === Phaser.LEFT)
{
this.pacman.scale.x = -1;
}
else if (direction === Phaser.UP)
{
this.pacman.angle = 270;
}
else if (direction === Phaser.DOWN)
{
this.pacman.angle = 90;
}
this.current = direction;
},*/
this.add.tween(this.pacman).to( { angle: this.getAngle(direction) }, this.turnSpeed, "Linear", true);
this.current = direction;
},
getAngle: function (to) {
// About-face?
if (this.current === this.opposites[to])
{
return "180";
}
if ((this.current === Phaser.UP && to === Phaser.LEFT) ||
(this.current === Phaser.DOWN && to === Phaser.RIGHT) ||
(this.current === Phaser.LEFT && to === Phaser.DOWN) ||
(this.current === Phaser.RIGHT && to === Phaser.UP))
{
return "-90";
}
return "90";
},
eatDot: function (pacman, dot) {
dot.kill();
var audio = new Audio('assets/pacman_chomp.wav');
audio.play()
this.score+=10;
this.scoreText.text= 'Score: ' + this.score;
//setTimeout(audio.play(),2000);
if (this.dots.total === 0)
{
this.dots.callAll('revive');
}
},
/*function muteAudio() {
var audio = document.getElementById('audioPlayer');
if (audio.mute = false) {
document.getElementById('audioPlayer').muted = true;
}
else {
audio.mute = true
document.getElementById('audioPlayer').muted = false;
}
}*/
eatCoin: function(pacman,coin){//pente
coin.kill();
this.score+=20;
this.scoreText.text= 'Score: ' + this.score;
var audio = new Audio('assets/pacman_eatfruit.wav');
audio.play()
if(this.coins.total===0)
{
this.coins.callAll('revive');
}
},
eatStr: function(pacman,str){//pente
str.kill();
this.bonus+=100;
this.bonusText.text= 'Bonus: ' + this.bonus;
var audio = new Audio('assets/pacman_eatfruit.wav');
audio.play()
if(this.strs.total===0)
{
this.strs.callAll('revive');
}
},
update: function () {
this.physics.arcade.collide(this.pacman, this.layer);
this.physics.arcade.overlap(this.pacman, this.dots, this.eatDot, null, this);
this.physics.arcade.overlap(this.pacman, this.dots, this.eatCoin, null, this);
this.physics.arcade.overlap(this.pacman, this.dots, this.eatStr, null, this);
this.marker.x = this.math.snapToFloor(Math.floor(this.pacman.x), this.gridsize) / this.gridsize;
this.marker.y = this.math.snapToFloor(Math.floor(this.pacman.y), this.gridsize) / this.gridsize;
// Update our grid sensors
this.directions[1] = this.map.getTileLeft(this.layer.index, this.marker.x, this.marker.y);
this.directions[2] = this.map.getTileRight(this.layer.index, this.marker.x, this.marker.y);
this.directions[3] = this.map.getTileAbove(this.layer.index, this.marker.x, this.marker.y);
this.directions[4] = this.map.getTileBelow(this.layer.index, this.marker.x, this.marker.y);
this.checkKeys();
if (this.turning !== Phaser.NONE)
{
this.turn();
}
}
};
game.state.add('Game', Pacman, true);
</script>
</body>
</html>
So, can anyone help me with making the str invisible after 10 seconds?
You were pretty close.
I'm going to use pacman as an example, since I'm not 100% sure how you were going to fade out the coins. When Pacman eats them?
First, you want the following fadePicture function:
fadePicture: function() {
game.add.tween(this.pacman).to( { alpha: 0 }, 2000, Phaser.Easing.Linear.None, true);
},
Note how that was changed from function fadePicture().
Next, in your create function you can refer to it:
this.game.time.events.add(Phaser.Timer.SECOND * 4, this.fadePicture, this);
That replaces the following:
//game.time.events.add(Phaser.Timer.SECOND * 4, fadePicture, this);
I've created a JSFiddle with the relevant changes.
If you do want your coins to fade then I would try updating fadePicture to accept a dot/str parameter and then game.add.tween on that.
I've read and tried probably every thread on angular $watch() DOM element height but can't work out how to do this. Any help is greatly appreciated!
I have an angular app that does a simple class name update by changing a model value. Example:
class="theme-{{themeName}}"
When the class updates the DIV changes height.
I want to receive a callback on the height change.
I've tried to use $watch() and $watch(..,,true) and using both angular.element() as well as jquery ( $('foo')... ) but the $digest cycle never even calls the $watch expression.
Update (code example):
'use strict';
angular.module('k2')
.directive('k2', ['$rootScope', '$templateCache', '$timeout', 'lodash' ,'k2i',
function ($rootScope, $templateCache, $timeout, lodash, k2i) {
return {
restrict: 'E',
template: $templateCache.get('k2/templates/k2.tpl.html'),
replace: true,
scope: {
ngShow: '=',
ngHide: '=',
settings: '='
},
link: function(scope, elem, attrs) {
k2i.initK2(scope, scope.settings || {});
scope.$watch(function() {
return $('.k2 .k2-template [k2-name]').height();
}, function(newValue, oldValue, scope) {
respondToChange(newValue, oldValue, scope);
}, true);
scope.$watch(function() {
var kb = document.querySelectorAll('.k2 .k2-template [k2-name]')[0];
var ab = document.querySelectorAll('.k2 .k2-template [k2-name] .k2-acc-bar')[0];
var value = {
kb: 0,
ab: 0
}
if (kb) {
value.kb = kb.clientHeight;
}
if (ab) {
value.ab = ab.clientHeight;
}
return value;
}, function(newValue, oldValue, scope) {
respondToChange(newValue, oldValue, scope);
}, true);
function respondToChange(newValue, oldValue, scope) {
if (newValue === oldValue) return;
if (!scope.k2Pending) return;
var kbNode = document.querySelectorAll('.k2 .k2-template [k2-name="' + scope.k2Pending.name + '"]');
var abNode = document.querySelectorAll('.k2 .k2-template [k2-name="' + scope.k2Pending.name + '"] .k2-acc-bar');
// Ensure required keyboard elements are in the DOM and have height.
if ((kbNode.length > 0 && !scope.k2Pending.requiresAccessoryBar ||
kbNode.length > 0 && abNode.length > 0 && scope.k2Pending.requiresAccessoryBar) &&
(kbNode[0].clientHeight > 0 && !scope.k2Pending.requiresAccessoryBar ||
kbNode[0].clientHeight > 0 && abNode[0].clientHeight > 0 && scope.k2Pending.requiresAccessoryBar)) {
$rootScope.$emit('K2KeyboardInDOM', scope.k2Pending.name, getHeight());
}
};
function getHeight() {
var height = {};
var kbElem = angular.element(document.querySelectorAll('.k2')[0]);
var wasHidden = kbElem.hasClass('ng-hide');
kbElem.removeClass('ng-hide');
height[k2i.modes.NONE] = 0;
height[k2i.modes.ALL] = document.querySelectorAll('.k2 .k2-template [k2-name="' + scope.k2Name + '"]')[0].clientHeight;
height[k2i.modes.ACCESSORY_BAR_ONLY] = document.querySelectorAll('.k2 .k2-template [k2-name="' + scope.k2Name + '"] .k2-acc-bar')[0].clientHeight;
height[k2i.modes.KEYBOARD_KEYS_ONLY] = height[k2i.modes.ALL] - height[k2i.modes.ACCESSORY_BAR_ONLY];
if (wasHidden) {
kbElem.addClass('ng-hide');
}
return height;
};
}
}
}
]);
You should simply watch the 'themeName' variable. Then do your calculations in $timeout. $timeout should be required to wait your manual DOM updates. For ex:
scope.$watch('k2Name', function(newValue, oldValue){
$timeout(function(){
//do what you want
});
});
I am testing following directive in angular Jasmine unit test
angular.module('components.privileges', [])
.directive('hasPrivilege', function(privileges) {
return {
link: function (scope, element, attrs, $rootScope) {
if (!angular.isString(attrs.hasPrivilege)) {
throw "hasPrivilege value must be a string";
}
function toggleVisibilityBasedOnPrivilege() {
var hasPrivilege = privileges.evaluatePrivilegeExpression(attrs.hasPrivilege.trim());
console.log(hasPrivilege);
if (hasPrivilege) {
element.show();
} else {
element.hide();
}
}
toggleVisibilityBasedOnPrivilege();
$rootScope.$on('privilegesChanged', function(event, data){
toggleVisibilityBasedOnPrivilege();
});
}
};
}).factory('privileges', function ($rootScope) {
var privilegesList = {};
return {
clearPrivileges: function () {
privilegesList = {};
},
setPrivileges: function (privileges) {
privilegesList["PRIVILEGES_SET_FLAG"] = true;
if(angular.isDefined(privileges) && angular.isArray(privileges) && privileges.length > 0){
for(var p in privileges) {
var k = privileges[p];
if( k && angular.isString(k) && k.trim()!==""){
privilegesList[privileges[p]] = true;
}
}
}
$rootScope.$emit('privilegesChanged');
},
privilegesLoaded: function () {
return angular.isDefined(privilegesList["PRIVILEGES_SET_FLAG"]);
},
hasPrivilege: function (p) {
var k = p && angular.isString(p) ? p.trim() : "";
return p === "NA" || ( k!=="" && angular.isDefined(privilegesList[k]) );
},
evaluatePrivilegeExpression: function (exp) {
var isPrivileged = false;
if( exp !== undefined && exp !== null && angular.isString(exp)){
exp = exp.trim();
if(exp !== "" && exp !== "!"){
var value = exp;
var notPrivilegeFlag = value[0] === '!';
if (notPrivilegeFlag) {
value = value.slice(1).trim();
}
var hasPrivilege = this.hasPrivilege(value);
isPrivileged = (hasPrivilege && !notPrivilegeFlag || !hasPrivilege && notPrivilegeFlag);
}
}
return isPrivileged;
}
};
});
In the directive, when i call setprivileges, I use '$rootScope.$emit('privilegesChanged');' event to notify that the privileges are changed. If, my div does not have the privilege, it must be hidden otherwise available.
And my test case is as follows:-
describe("directive: hasPrivilege: ",function(){
var element, scope, priviledgesArray, $privilegesFactory, $compile;
beforeEach(function(){
module("components.privileges");
inject(function(privileges, _$rootScope_, _$compile_) {
$privilegesFactory = privileges;
scope= _$rootScope_;
$compile = _$compile_ ;
});
priviledgesArray = ["123"];
});
it("should hide the element when privileges have not been set", function(){
element = $compile('<div has-privilege="123"></div>')(scope);
scope.$digest();
$privilegesFactory.setPrivileges(priviledgesArray);
console.log($(element).find("div"));
expect($(element).find("div").style.display).toBe("block");
});
});
});
When i ran the test case, I got following error
TypeError: 'undefined' is not a function (evaluating 'element.hide()')
I am not getting what is the issue. Can someone help?
Try referencing element as an array in the directive, i.e. element[0].show(); and element[0].hide();.