Dynamically changing variables within object dependent on DOM - javascript

I would like to have an object that returns status true/false based on the presence of an element in the DOM. I can't get the variable to actually maintain a state other than the state of the loaded page. My last attempt was rather stupid and resulted in Maximum call stack size exceeded, which was expected. Any way that I could have these variables change with the DOM?
var navigation = function runNav(){
runNav();
var navigation = {
globe : {
element : $("#icons .globe"),
status : (function(){
return ($(".window_stable.globe").length == 1) ? true : false;
})()
},
cart : {
element : $("#icons .cart"),
status : (function(){
return ($(".window_stable.cart").length == 1) ? true : false
})()
},
phone : {
element : $("#icons .phone"),
status : (function(){
return ($(".window_stable.phone").length == 1) ? true : false
})()
}
}
return navigation;
}();
example
navigation.cart.status
false
-- Enabled the element
navigation.cart.status
false
($(".window_stable.cart").length == 1) ? true : false
true

You should remove the call to runNav() at the start of your code. It's causing an infinite recursion loop. Other than that, your code looks fine.

I think I get what you're trying to do. I think you want the function you've defined for status to run every time you access the status property.
To my knowledge, I don't think you can do it just by making it a property. I think you need to make it a function.
Like this: (jsFiddle)
var navigation = {
globe: {
element: $("#icons .globe"),
getStatus: function() { return ($(".window_stable.globe").length > 0); }
},
cart: {
element: $("#icons .cart"),
getStatus: function() { return ($(".window_stable.cart").length > 0); }
},
phone: {
element: $("#icons .phone"),
getStatus: function() { return ($(".window_stable.phone").length > 0); }
}
};
alert("status = "+ navigation.cart.getStatus());

The problem that you are seeing is because the "status" property is being set immediately. So if the navigation object is initialized before these elements are added to the DOM, "status" will always be false.
If you are dead set on accessing "status" like you would a property, you can use the Object.defineProperty pattern: http://jsfiddle.net/TFVFS/
var navigation = (function runNav(){
var navigation = {
globe : {
element : $("#icons .globe")
},
cart : {
element : $("#icons .cart")
},
phone : {
element : $("#icons .phone")
}
};
Object.defineProperty(navigation.globe, "status", {
get:function(){return $(".window_stable.globe").length == 1 ? true : false;}
});
Object.defineProperty(navigation.cart, "status", {
get:function(){return $(".window_stable.cart").length == 1 ? true : false;}
});
Object.defineProperty(navigation.phone, "status", {
get:function(){return $(".window_stable.phone").length == 1 ? true : false;}
});
return navigation;
})();
console.log(navigation.cart.status);
console.log(navigation.globe.status);
console.log(navigation.phone.status);
This isn't fully backwards compatible, and isn't fully supported in IE prior to version 9.
#Travesty3's answer will be backwards compatible, but it requires a change in syntax. Personally, I would recommend his answer if you need to support version of IE before 9.

Related

How can I use ng-if to display a button based on the current user?

I am create a user profile and would like to show an edit button that only displays for the profile's owner. I am attempting to use ng-if so that the edit capabilities are removed from the DOM.
How can I use ng-if to display a button based on the current user?
In my view:
<button ng-if="editProfile()">EDIT</button>
In my controller:
$scope.editProfile = function (canedit) {
var canedit = false;
for (var i=0;i<$scope.users.length;i++) {
if ($scope.users[i].id===$scope.currentUser.id) {
canedit = true;
}
}
}
Your function needs to return true or false
<button ng-if="editProfile()">EDIT</button>
$scope.editProfile = function () {
for (var i=0;i<$scope.users.length;i++) {
if ($scope.users[i].id===$scope.currentUser.id) {
return true
}
}
return false
}
You are returning nothing from your function.
You can make it this way
<button ng-if="canEdit()">EDIT</button>
and in your controller
$scope.canEdit = function () {
return $scope.users.find(u=>u.id === $scope.currentUser.id) ? true : false;
}
Just add
return canedit;
below the for closing bracket
As everyone mentioned, the function wasn't returning true or false. After adding the return I discovered that the for statement was extraneous and I just needed a simple check. This code is working correctly:
$scope.editProfile = function () {
if ($scope.user.id===$scope.currentUser.id) {
return true
}
return false
}

Programmatic Dijit/Tree Not Appearing in Declarative Dijit/ContentPane

Can anyone help me figure out why this works in Dojo 1.8 but not in 1.9?
In 1.8, the tree is placed within the “pilotTreeContainer” contentpane. In 1.9, the tree is there if you look in Firebug but visually, it just shows a loading graphic. pilotTreeContainer is declared in the template file for the widget that contains this code. All this code is in the postCreate method.
var treeStore = lang.clone( store );
var treeModel = new ObjectStoreModel(
{
store: treeStore,
query: { id: 'all' },
mayHaveChildren: function ( item ) // only ships have the unique attribute
{
return item.unique === undefined;
}
} );
var pilotTree = new Tree(
{
model: treeModel, // do we need to clone?
autoExpand: true,
showRoot: false,
title: 'Click to add',
openOnClick: true,
getDomNodeById: function ( id ) // new function to find DOM node
{
if ( this._itemNodesMap !== undefined && this._itemNodesMap[ id ] !== undefined && this._itemNodesMap[ id ][0] !== undefined ) {
return this._itemNodesMap[ id ][0].domNode;
}
else {
return null;
}
}
} );
this.pilotTree = pilotTree;
this.pilotTreeContainer.set( 'content', pilotTree );
I’ve tried calling startup on both tree and contentpane.
Debugging the dijit/Tree code, it seesm that there is a deferred which never resolves. It is returned from the _expandNode function when called from the _load function (when trying to expand the root node this._expandNode(rn).then).
The part that fails in dijit/Tree is this:
// Load top level children, and if persist==true, all nodes that were previously opened
this._expandNode(rn).then(lang.hitch(this, function(){
// Then, select the nodes specified by params.paths[].
this.rootLoadingIndicator.style.display = "none";
this.expandChildrenDeferred.resolve(true);
}));
Why is the tree not showing? What is going wrong?
Coming back to this (in the hope that it would be solved in Dojo 1.10), I have found a fix.
I abstracted the tree into its own module, adding it to the container with placeAt() instead of using this.pilotTreeContainer.set( 'content', pilotTree );:
// dijit/Tree implementation for pilots
pilotTree = new PilotTree(
{
model: treeModel
} );
// add to container
pilotTree.placeAt( this.pilotTreeContainer.containerNode );
pilotTree.startup();
then forced it to show its content within the tree's startup() method:
startup: function()
{
this.inherited( arguments );
// force show!
this.rootLoadingIndicator.style.display = "none";
this.rootNode.containerNode.style.display = "block";
},

ExtJS 4.2 - Tab Selects Wrong Value in Combobox with typeAhead:true when Typing Fast

When a combobox is used in ExtJS (tested in 4.2, but likely other versions as well), and the "typeAhead: true" option is set, if you the user types in values very quickly and then hits the "tab" on their keyboard, the focus will move to the next field on the screen and the wrong value is set. Because of the tricky nature of this bug, I have created a JSFiddle for it here: http://jsfiddle.net/59AVC/2/
To replicate the bug, very quickly type "975" and then "tab" in the first combobox field. If you hit tab very quickly after you enter the "5" in "975", you will see that the combobox is set to the "970" option instead. I believe this is happening because the "Tab" is causing whatever option is highlighted in the list to be the value that is set, but what is strange is that the "970" is highlighted still after the "5" in "975" is entered, when it should process that event first before the "tab" and it should have changed the selection to be the correct "975".
I have tried adjusting the typeAheadDelay (set to 0 in the example), as well as the queryDelay and everything else I can think of. It looks like ExtJS is simply canceling the lookup that is somehow still running and not finished when the tab is pressed.
Any suggestions on how to work around this bug? Do I need to write my own "typeAhead" auto-complete function to handle this correctly by single threading the events?
Here is the sample JSFiddle code that shows this:
// The data store containing the list of values
var states = Ext.create('Ext.data.Store', {
fields: ['val_number', 'val_name'],
data : [
{"val_number":"970", "val_name":"970 - Name"},
{"val_number":"971", "val_name":"971 - Name"},
{"val_number":"972", "val_name":"972 - Name"},
{"val_number":"973", "val_name":"973 - Name"},
{"val_number":"974", "val_name":"974 - Name"},
{"val_number":"975", "val_name":"975 - Name"}
//...
]
});
Ext.create('Ext.form.ComboBox', {
fieldLabel: 'Choose 1st Value',
store: states,
queryMode: 'local',
displayField: 'val_name',
valueField: 'val_number',
renderTo: Ext.getBody(),
typeAhead: true,
typeAheadDelay: 0,
minChars: 1,
forceSelection: true,
autoSelect: false,
triggerAction: 'all',
queryDelay: 0,
queryCaching: false
});
Ext.create('Ext.form.ComboBox', {
fieldLabel: 'Choose 2nd Value',
store: states,
queryMode: 'local',
displayField: 'val_name',
valueField: 'val_number',
renderTo: Ext.getBody(),
typeAhead: true,
typeAheadDelay: 0,
minChars: 1,
forceSelection: true,
autoSelect: false,
triggerAction: 'all',
queryDelay: 0,
queryCaching: false
});
UPDATED:
Tried this code as suggested, no change in result - still doesn't select correctly:
Ext.define('App.CustomComboBox', {
extend: 'Ext.form.field.ComboBox',
alias: 'widget.CustomCombobox',
initComponent:function() {
// call parent init component
this.callParent(arguments);
},
onTypeAhead: function() {
console.log('onTypeAhead...');
var me = this,
displayField = me.displayField,
record = me.store.findRecord(displayField, me.getRawValue()),
boundList = me.getPicker(),
newValue, len, selStart;
if (record) {
newValue = record.get(displayField);
len = newValue.length;
selStart = me.getRawValue().length;
//boundList.highlightItem(boundList.getNode(record));
if (selStart !== 0 && selStart !== len) {
me.setRawValue(newValue);
me.selectText(selStart, newValue.length);
}
}
}
});
found the problematic code snippet:
beforeBlur: function() {
this.doQueryTask.cancel();
this.assertValue();
},
the problem is not typeAhead its selectOnTab together with autoSelect which will be set to true from typeahead.
so this happens:
you type "97" a query will fire and the first value (970) will be selected
you type "5" a query will start, but
you press "tab" and the beforeBlur function gets executed.
the query gets canceled and the currently highlighted value becomes the field value
so what can you do?
it is not possible to give the task a callback so assertValue is called after the query finishes :(
you need to disable autoselect again and the only way i found is to override onTypeAhead and to comment out the highlighting:
.
onTypeAhead: function() {
var me = this,
displayField = me.displayField,
record = me.store.findRecord(displayField, me.getRawValue()),
boundList = me.getPicker(),
newValue, len, selStart;
if (record) {
newValue = record.get(displayField);
len = newValue.length;
selStart = me.getRawValue().length;
//boundList.highlightItem(boundList.getNode(record));
if (selStart !== 0 && selStart !== len) {
me.setRawValue(newValue);
me.selectText(selStart, newValue.length);
}
}
}
Thanks to Jandalf, I have some good news. I was able to work out a solution for my needs by extending the combobox and introducing a few fixes. The first was to do as Jandalf suggested (a good starting point) and the next set of fixes was to stop using a DelayedTask if the delay was 0 or less (my config sets "typeAheadDelay" and "queryDelay" to 0). Finally, I had to also do a check in the "assertValue" that is the equivalent of what happens when someone types a regular key to catch the problem where the tab is blurring things before the keyUp is done. Because of this last part, it may not be the perfect solution for everyone, but it was the only thing that could solve my problem. So, here is the code that makes it work for me. I hope someone else will find it useful.
Ext.define('App.CustomComboBox', {
extend: 'Ext.form.field.ComboBox',
alias: 'widget.CustomCombobox',
initComponent:function() {
// call parent init component
this.callParent(arguments);
},
onTypeAhead: function() {
var me = this,
displayField = me.displayField,
record = me.store.findRecord(displayField, me.getRawValue()),
boundList = me.getPicker(),
newValue, len, selStart;
if (record) {
newValue = record.get(displayField);
len = newValue.length;
selStart = me.getRawValue().length;
//Removed to prevent onBlur/Tab causing invalid selections
//boundList.highlightItem(boundList.getNode(record));
if (selStart !== 0 && selStart !== len) {
me.setRawValue(newValue);
me.selectText(selStart, newValue.length);
}
}
},
onPaste: function(){
var me = this;
if (!me.readOnly && !me.disabled && me.editable) {
if (me.queryDelay > 0) {
//Delay it
me.doQueryTask.delay(me.queryDelay);
} else {
//Changed to do immediately instead of in the delayed task
me.doRawQuery();
}
}
},
// store the last key and doQuery if relevant
onKeyUp: function(e, t) {
var me = this,
key = e.getKey();
if (!me.readOnly && !me.disabled && me.editable) {
me.lastKey = key;
// we put this in a task so that we can cancel it if a user is
// in and out before the queryDelay elapses
// perform query w/ any normal key or backspace or delete
if (!e.isSpecialKey() || key == e.BACKSPACE || key == e.DELETE) {
if (me.queryDelay > 0) {
//Delay it
me.doQueryTask.delay(me.queryDelay);
} else {
//Changed to do immediately instead of in the delayed task
me.doRawQuery();
}
}
}
if (me.enableKeyEvents) {
me.callParent(arguments);
}
},
// private
assertValue: function() {
var me = this,
value = me.getRawValue(),
rec, currentValue;
if (me.forceSelection) {
if (me.multiSelect) {
// For multiselect, check that the current displayed value matches the current
// selection, if it does not then revert to the most recent selection.
if (value !== me.getDisplayValue()) {
me.setValue(me.lastSelection);
}
} else {
// For single-select, match the displayed value to a record and select it,
// if it does not match a record then revert to the most recent selection.
rec = me.findRecordByDisplay(value);
if (rec) {
currentValue = me.value;
// Prevent an issue where we have duplicate display values with
// different underlying values.
if (!me.findRecordByValue(currentValue)) {
me.select(rec, true);
}
} else {
//Try and query the value to find it as a "catch" for the blur happening before the last keyed value was entered
me.doRawQuery();
//Get the new value to use
value = me.getRawValue();
//Copy of the above/same assert value check
rec = me.findRecordByDisplay(value);
if (rec) {
currentValue = me.value;
// Prevent an issue where we have duplicate display values with
// different underlying values.
if (!me.findRecordByValue(currentValue)) {
me.select(rec, true);
}
} else {
//This is the original "else" condition
me.setValue(me.lastSelection);
}
}
}
}
me.collapse();
},
doTypeAhead: function() {
var me = this;
if (!me.typeAheadTask) {
me.typeAheadTask = new Ext.util.DelayedTask(me.onTypeAhead, me);
}
if (me.lastKey != Ext.EventObject.BACKSPACE && me.lastKey != Ext.EventObject.DELETE) {
//Changed to not use the delayed task if 0 or less
if (me.typeAheadDelay > 0) {
me.typeAheadTask.delay(me.typeAheadDelay);
} else {
me.onTypeAhead();
}
}
}
});

Setting AJAX = "false" in this code

I have this code for the previous and next buttons but when I use them they do not return any data unless I refresh the page. Is it as simple as adding some code to this to turn ajax to false or is there a better solution? And how should this be handled? Thanks.
$links.each(function)
{
var reverse = $(this).closest("." + prevLIClass).length;
$(this).buttonMarkup(
{
"role" : "button",
"theme" : "d",
"iconpos" : "notext",
"icon" : "arrow-" + (reverse ? "1" : "r")
})
.bind("vclick", function()
{
$.mobile.changePage($(this).attr("href"),{reverse : reverse});
return false;
});
});

How to remove MenuItemTitle from TinyMCE?

I have enabled the 'fontsizeselect' plugin in tinyMCE. My question is how do I remove the header (title) of the drop-down menu?
Edit:
I've tried removing it using JQuery .remove(), but after that the height of whole list is calculated wrong.
The second option I tried was:
http://www.tinymce.com/wiki.php/API3:method.tinymce.ui.DropMenu.remove
But that just went wrong and "fontsizeselect.remove(title)" (analogically to .add) makes error to whole tinyMCE - "missing : after property id". Problably it is completly bad method to do this.
The third option was editing tiny_mce\themes\advanced\editor_template_src.js line 467:
c = ed.controlManager.createListBox('fontsizeselect', {title : 'advanced.font_size', onselect : function(v) {...}
but seems, that TinyMCE developers thought, that every drop-down must have title/header
SOLVED:
before initialization of MCE we have to override the menu rendering function
(function(tinymce) {
var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher, undef;
tinymce.create('tinymce.ui.ListBoxNoTitle:tinymce.ui.ListBox', {
renderMenu : function() {
var t = this, m;
m = t.settings.control_manager.createDropMenu(t.id + '_menu', {
menu_line : 1,
'class' : t.classPrefix + 'Menu mceNoIcons',
max_width : 250,
max_height : 150
});
m.onHideMenu.add(function() {
t.hideMenu();
t.focus();
});
/* m.add({
title : t.settings.title,
'class' : 'mceMenuItemTitle',
onclick : function() {
if (t.settings.onselect('') !== false)
t.select(''); // Must be runned after
}
});
*/
each(t.items, function(o) {
// No value then treat it as a title
if (o.value === undef) {
m.add({
title : o.title,
role : "option",
'class' : 'mceMenuItemTitle',
onclick : function() {
if (t.settings.onselect('') !== false)
t.select(''); // Must be runned after
}
});
} else {
o.id = DOM.uniqueId();
o.role= "option";
o.onclick = function() {
if (t.settings.onselect(o.value) !== false)
t.select(o.value); // Must be runned after
};
m.add(o);
}
});
t.onRenderMenu.dispatch(t, m);
t.menu = m;
}
});
})(tinymce);
And with this comment on "m.add" You just have to add
tinyMCE.init({
setup : function(ed) {
ed.onBeforeRenderUI.add(function(ed){
ed.controlManager.setControlType('listbox', tinymce.ui.ListBoxNoTitle);
});
}
});
this setup to standard initialization of tinyMCE. So, it can be done without editing source files.

Categories

Resources