We are experiencing an issue when using the lazyloading feature and open_all feature together.
The contents of the tree are loaded using lazy loading feature.
When we select a node and click on expand all button, all the child nodes of that node will be fetched using jstree ajax call and opened using the open_all function, when clicking collapse all button, we are using close_all function. This works perfectly for the first time.
But on second time, when we click on expand all on the same node, same ajax url is hit on recursively. ( We think, the url is hit on every time on opening a node using open_all). The intended behaviour is not to call the url(as data is already loaded), only the open_all function should be executed.
Could you please clarify how to fix the issue
//code to load the tree
$("#TreePanel").jstree({
"xml_data" : {
"ajax" : {
"url" : "/ajax/loadTree",
"type" : "post",
"data" : function(node) {
var data = {};
data.dunsNumber = ${dunsNumber};
if (node == -1) {
//set duns number to data
} else {
data.selectedNodeId = node.attr("id");
data.expandAll = isExpandAll;
}
return data;
},
"success" : function(data) {
if ($(data).attr('id') == 'error') {
$("#overlayContent").empty();
} else {
return data;
}
}
},
"xsl" : "nest"
},
"plugins" : [ "themes", "xml_data" ]
});
//code to expand all nodes
$("#ufvExpandAll").bind("click", function() {
isExpandAll= true;
$("#TreePanel").jstree("open_all", selectedNode);
isExpandAll= false;
});
//code to collapse all nodes
$("#ufvCollapseAll").bind("click", function() {
$("#TreePanel").jstree("close_all", selectedNode);
});
//code to get the node and set on a variable on clicking a node
var selectedNode;
$("#TreePanel").delegate("a", "click", function(e, data) {
var node = $(e.target).closest("li");
if (selectedNode != undefined && selectedNode != null) {
$("#" + selectedNode.id + " > a").removeClass("jstree-default- selected-node");
}
selectedNode = node[0];
$("#" + selectedNode.id + " > a").addClass("jstree-default-selected- node");
$("#ufvExpandAll").attr("disabled", false);
$("#ufvCollapseAll").attr("disabled", false);
return false;
});
Thanks In Advance
Regards
Hari
Try to inspect the node to see if it has any pre-existing children before executing logic to load it with children.
.bind("open_node.jstree", function (event, data) {
var node = $(data.rslt.obj);
var children = $.jstree._reference(node)._get_children(node);
if (children.length==0){
//node is empty, so do node open logic here
}
})
Related
I am trying to change this demo:
http://maxwells.github.io/bootstrap-tags.html
into a responsive version in which I can set it to readOnly and remove it from readOnly as I like. This code:
var alltags = ["new tag", "testtag", "tets", "wawa", "wtf", "wtf2"];
$(document).ready(function() {
var tagbox = $('#my-tag-list').tags({
suggestions: alltags
});
var tagenable = true;
$('#my-tag-list').focusout(function() {
if (tagenable) {
tagbox.readOnly = true;
$('#my-tag-list').empty();
tagbox.init();
tagenable = false;
}
});
$('#my-tag-list').click(function() {
if(!tagenable) {
tagbox.readOnly = false;
$('#my-tag-list').empty();
tagbox.init();
tagenable = true;
}
});
});
seems to work fairly well, it makes everything readonly after focusout and editable when I click it. However, the editing does not work since I cannot insert new tags nor delete them (seems to be like event handling was lost or something like that).
I am guessing that emptying the #my-tag-list div is causing this, but I cannot yet find a way to use for instance "detach" instead that removes everything inside (not the element itself) and putting it back in again.
I tried to make a JS Fiddle, but it isn't really working so well yet:
http://jsfiddle.net/tomzooi/cLxz0L06/
The thing that does work is a save of the entire website, which is here:
https://www.dropbox.com/sh/ldbfqjol3pppu2k/AABhuJA4A6j9XTxUKBEzoH6za?dl=0
this link has the unminimized JS of the bootstrap-tags stuff I am using:
https://github.com/maxwells/bootstrap-tags/blob/master/dist/js/bootstrap-tags.js
So far I managed to do this with some modifications of the bootstrap javascript code. I use two different tagbox which I hide and unhide with some click events.
var tagbox = $('#my-tag-list').tags({
suggestions: alltags,
tagData: tmp_tags,
afterAddingTag: function(tag) { tagboxro.addTag(tag); },
afterDeletingTag: function(tag) {tagboxro.removeTag(tag); }
});
var tagboxro = $('#my-tag-listro').tags({
suggestions: alltags,
tagData: tmp_tags,
readOnly: 'true',
tagSize: 'sm',
tagClass: 'btn-info pull-right'
});
$(document).mouseup(function (e) {
var container = $("#my-tag-list");
if (!container.is(e.target) // if the target of the click isn't the container...
&& container.has(e.target).length === 0) { // ... nor a descendant of the container
if (tagsave) {
$("#my-tag-listro").show();
$("#my-tag-list").hide();
var tags = tagbox.getTags();
$.post("%basedir%/save.php", {
editorID:"new_tags",
tags:tags
}, function(data,status){
//alert("Data: " + data + "\nStatus: " + status);
});
tagsave = false;
}
}
});
$('#my-tag-listro').click(function() {
tagsave = true;
//$(".tag-list").toggle();
$("#my-tag-list").show();
$("#my-tag-listro").hide();
});
I had to modify the code of bootstrap-tags.js to allow for this since it normally deletes all of the usefull functions when it is considered readonly in the init function:
if (this.readOnly) {
this.renderReadOnly();
this.removeTag = function(tag) {
if (_this.tagsArray.indexOf(tag) > -1) {
_this.tagsArray.splice(_this.tagsArray.indexOf(tag), 1);
_this.renderReadOnly();
}
return _this;
};
this.removeTagClicked = function() {};
this.removeLastTag = function() {};
this.addTag = function(tag) {
_this.tagsArray.push(tag);
_this.renderReadOnly();
return _this;
};
this.addTagWithContent = function() {};
this.renameTag = function() {};
return this.setPopover = function() {};
}
would be awesome if this feature was incorporated in a somewhat less hacky way though :)
I've got a file which needs to run on page load (randomise_colors.js), but also needs to be called by another file as part of a callback function (in infinite_scroll.js). The randomise_colors script just loops through a list of posts on the page and assigns each one a color from an array which is used on the front-end.
Infinite Scroll loads new posts in to the DOM on a button click, but because the randomise_colors.js file has already ran on page load, new content loaded is not affected by this so I need it to run again. I'm open to other suggestions if it sounds like I could be tackling the problem in a different way, I'm no JS expert.
Currently I'm getting Uncaught ReferenceError: randomise_colours is not defined referring this line of infinite_scroll.js:
randomise_colours.init();
I'm calling all files that need be loaded on document.ready in app.js
require(['base/randomise-colours', 'base/infinite-scroll'],
function(randomise_colours, infinite_scroll) {
var $ = jQuery;
$(document).ready(function() {
infinite_scroll.init();
randomise_colours.init();
});
}
);
This is infinite_scroll.js which initialises Infinite Scroll and features the callback. The callback function runs whenever new items are loaded in via AJAX using the Infinite Scroll jQuery plugin. I've put asterix around the area where I need to run the randomise_colors.init() function from randomise_colors.js.
define(['infinitescroll'], function() {
var $ = jQuery,
$loadMore = $('.load-more-posts a');
function addClasses() {
**randomise_colours.init();**
};
return {
init: function() {
if($loadMore.length >= 1) {
this.setUp();
} else {
return false;
}
},
setUp: function() {
this.initInfiniteScroll();
},
initInfiniteScroll: function() {
$('.article-listing').infinitescroll({
navSelector : '.load-more-posts',
nextSelector : '.load-more-posts a',
itemSelector : '.standard-post'
}, function(newItems) {
addClasses();
});
//Unbind the standard scroll-load function
$(window).unbind('.infscr');
//Click handler to retrieve new posts
$loadMore.on('click', function() {
$('.article-listing').infinitescroll('retrieve');
return false;
});
}
};
});
And this is my randomise_colors.js file which runs fine on load, but needs to be re-called again after new content has loaded in.
define([], function() {
var $ = jQuery,
$colouredSlide = $('.image-overlay'),
colours = ['#e4cba3', '#867d75', '#e1ecb9', '#f5f08a'],
used = [];
function pickRandomColour() {
if(colours.length == 0) {
colours.push.apply(colours, used);
used = [];
}
var selected = colours[Math.floor(Math.random() * colours.length)];
var getSelectedIndex = colours.indexOf(selected);
colours.splice(getSelectedIndex, 1);
used.push(selected);
return selected;
};
return {
init: function() {
if($colouredSlide.length >= 1) {
this.setUp();
} else {
return false;
}
},
setUp: function() {
this.randomiseColours();
},
randomiseColours: function() {
console.log('randomise');
$colouredSlide.each(function() {
var newColour = pickRandomColour();
$(this).css('background', newColour);
});
}
};
});
You would have to reference randomiseColours inside the infiniteScroll file. So you need to change your define function to the following:
define(['infinitescroll', 'randomise-colours'], function(infiniteScroll, randomise_colours)
Remember that when using require you need to reference all variables through the define function, otherwise they will not be recognised.
I'm using this jQuery script to show search results. Everything works fine, but when search results have more than one page and I'm browsing pages via paging then every page loading is gradually getting slower. Usually first cca 10 pages loads I get quickly, but next are getting avoiding loading delay. Whole website get frozen for a little while (also loader image), but browser is not yet. What should be the problem?
function editResults(def) {
$('.searchResults').html('<p class=\'loader\'><img src=\'images/loader.gif\' /></p>');
var url = def;
var url = url + "&categories=";
// Parse Categories
$('input[name=chCat[]]').each(function() {
if (this.checked == true) {
url = url + this.value + ",";
}
});
url = url + "&sizes=";
// Parse Sizes
$('input[name=chSize[]]').each(function() {
if (this.checked == true) {
url = url + this.value + ",";
}
});
url = url + "&prices=";
// Parse Prices
$('input[name=chPrice[]]').each(function() {
if (this.checked == true) {
url = url + this.value + ",";
}
});
$('.searchResults').load('results.php'+url);
$('.pageLinks').live("click", function() {
var page = this.title;
editResults("?page="+page);
});
}
$(document).ready(function(){
editResults("?page=1");
// Check All Categories
$('input[name=chCat[0]]').click(function() {
check_status = $('input[name=chCat[0]]').attr("checked");
$('input[name=chCat[]]').each(function() {
this.checked = check_status;
});
});
// Check All Sizes
$('input[name=chSize[0]]').click(function() {
check_status = $('input[name=chSize[0]]').attr("checked");
$('input[name=chSize[]]').each(function() {
this.checked = check_status;
});
});
// Edit Results
$('.checkbox').change(function() {
editResults("?page=1");
});
// Change Type
$(".sort").change(function() {
editResults("?page=1&sort="+$(this).val());
});
});
$('.pageLinks').live("click", function() {
var page = this.title;
editResults("?page="+page);
});
just a wild guess but... wouldn't this piece of code add a new event handler to the click event instead reaplacing the old one with a new one? causing the click to call all the once registered handlers.
you should make the event binding just once
var global_var = '1';
function editResults(def) {
// all your code
global_var = 2; // what ever page goes next
};
$(document).ready(function() {
// all your code ...
$('.pageLinks').live("click", function() {
var page = global_var;
editResults("?page="+page);
});
});
I'm using WYSIHTML5 Bootstrap ( http://jhollingworth.github.com/bootstrap-wysihtml5 ), based on WYSIHTML5 ( https://github.com/xing/wysihtml5 ) which is absolutely fantastic at cleaning up HTML when copy pasting from websites.
I'd like to be able to handle code into the editor, and then highlight the syntax with HighlightJS.
I've created a new button and replicated the method used in wysihtml5.js to toggle bold <b> on and off, using <pre> instead:
(function(wysihtml5) {
var undef;
wysihtml5.commands.pre = {
exec: function(composer, command) {
return wysihtml5.commands.formatInline.exec(composer, command, "pre");
},
state: function(composer, command, color) {
return wysihtml5.commands.formatInline.state(composer, command, "pre");
},
value: function() {
return undef;
}
};
})(wysihtml5)
But that's not enough. The editor hides the tags when editing. I need to be able to wrap my content in both <pre>and <code>ie. <pre><code></code></pre>.
This means writing a different function than the one used by wysihtml5, and I don't know how... Could anyone help me with that?
Here's the code for the formatInline function in wysihtml5:
wysihtml5.commands.formatInline = {
exec: function(composer, command, tagName, className, classRegExp) {
var range = composer.selection.getRange();
if (!range) {
return false;
}
_getApplier(tagName, className, classRegExp).toggleRange(range);
composer.selection.setSelection(range);
},
state: function(composer, command, tagName, className, classRegExp) {
var doc = composer.doc,
aliasTagName = ALIAS_MAPPING[tagName] || tagName,
range;
// Check whether the document contains a node with the desired tagName
if (!wysihtml5.dom.hasElementWithTagName(doc, tagName) &&
!wysihtml5.dom.hasElementWithTagName(doc, aliasTagName)) {
return false;
}
// Check whether the document contains a node with the desired className
if (className && !wysihtml5.dom.hasElementWithClassName(doc, className)) {
return false;
}
range = composer.selection.getRange();
if (!range) {
return false;
}
return _getApplier(tagName, className, classRegExp).isAppliedToRange(range);
},
value: function() {
return undef;
}
};
})(wysihtml5);
Got the answer from Christopher, the developer of wysihtml5:
wysihtml5.commands.formatCode = function() {
exec: function(composer) {
var pre = this.state(composer);
if (pre) {
// caret is already within a <pre><code>...</code></pre>
composer.selection.executeAndRestore(function() {
var code = pre.querySelector("code");
wysihtml5.dom.replaceWithChildNodes(pre);
if (code) {
wysihtml5.dom.replaceWithChildNodes(pre);
}
});
} else {
// Wrap in <pre><code>...</code></pre>
var range = composer.selection.getRange(),
selectedNodes = range.extractContents(),
pre = composer.doc.createElement("pre"),
code = composer.doc.createElement("code");
pre.appendChild(code);
code.appendChild(selectedNodes);
range.insertNode(pre);
composer.selection.selectNode(pre);
}
},
state: function(composer) {
var selectedNode = composer.selection.getSelectedNode();
return wysihtml5.dom.getParentElement(selectedNode, { nodeName: "CODE" }) && wysihtml5.dom.getParentElement(selectedNode, { nodeName: "PRE" });
}
};
... and add this to your toolbar:
<a data-wysihtml5-command="formatCode">highlight code</a>
Many thanks Christopher!!
I fork today bootstrap-wysihtml5 project and add code highlighting support using Highlight.js.
You can check demo at http://evereq.github.com/bootstrap-wysihtml5 and review source code https://github.com/evereq/bootstrap-wysihtml5. It's basically almost same code as from Christopher, together with UI changes and embeded inside bootstrap version of editor itself.
I have two dynatrees on the same page. Tree 1 has various number of nodes, tree 2 is empty. I would like to drag a node from tree 1 and drop it on tree 2. Everything works fine while tree 2 is not empty. If tree 2 is empty, however, it is not working at all. Here is my code:
$('#tree1').dynatree({
dnd: {
onDragStart: function (node) {
return true;
}
}
});
$('#tree2').dynatree({
dnd: {
onDragEnter: function (node, sourceNode) {
return true;
},
onDrop: function (node, sourceNode, hitMode, ui, draggable) {
var copyNode = sourceNode.toDict(true, function (dict) {
delete dict.key;
});
node.addChild(copyNode);
}
}
});
Can anyone tell me how to solve this problem? Thanx in advance.
After reading the dynatree source code (version 1.2.0), I found that it is not possible to add a node to an empty tree without the modification of the source code. This is because when you try to drop a source node to a target tree, it is actually drop a source node to/before/after a target node instead of target tree. Hence, dynatree will not work if there is no node in a tree.
I did something like this:
var tree1Dragging = false,
tree2Over = false;
$('#tree1').dynatree({
dnd : {
onDragStart : function(node) {
tree1Dragging = true;
return true;
},
onDragStop : function(node) {
critDrag = false;
if (tree2Over) {
//TODO do your drop to tree2 process here (get the root and append the "node" to it for example)
}
}
}
});
$('#tree2').dynatree({
dnd : {
onDragEnter : function(node, sourceNode) {
return true;
},
onDrop : function(node, sourceNode, hitMode, ui, draggable) {
var copyNode = sourceNode.toDict(true, function(dict) {
delete dict.key;
});
node.addChild(copyNode);
}
}
});
// Add mouse events to know when we are above the tree2 div
$("#tree2").mouseenter(function() {
if (tree1Dragging) {
tree2Over = true;
}
}).mouseleave(function(){
tree2Over = false;
});