undo drag and drop of item - dynatree

I have a full functional dynatree, where data gets load via ajax (database source), now i enabled drag and drop functionality for the tree. When an item gets dropped i do an ajax request to update the database so node data gets updated. But i do several checks in php to see if node is allowed to be placed on it's "new location".
How can i restore the original location of the node if php rejects the new location where the node is dropped? A full tree reload is possible, but i want to avoid that, and just restore the specific item.

Ok , i found a correct way to do this. I just do my ajax request to php but set it to an sync (async false), so the dynatree code will wait for the result. Then i implemented a check around the move function of dynatree:
$.ajax({
url: "xxx,
async: false,
success: function(data){
if(data.error == 0){
showModalOk(data.message);
phpres = true;
}else
phpres = false;
}
}
});
if(phpres == true){
sourceNode.move(node, hitMode);
node.expand(true);
}
that way the node only gets moved if it's allowed, so no need to restore the node afterwards

Related

How do you poll for a condition in Intern / Leadfoot (not browser / client side)?

I'm trying to verify that an account was created successfully, but after clicking the submit button, I need to wait until the next page has loaded and verify that the user ended up at the correct URL.
I'm using pollUntil to check the URL client side, but that results in Detected a page unload event; script execution does not work across page loads. in Safari at least. I can add a sleep, but I was wondering if there is a better way.
Questions:
How can you poll on something like this.remote.getCurrentUrl()? Basically I want to do something like this.remote.waitForCurrentUrlToEqual(...), but I'm also curious how to poll on anything from Selenium commands vs using pollUntil which executes code in the remote browser.
I'm checking to see if the user ended up at a protected URL after logging in here. Is there a better way to check this besides polling?
Best practices: do I need to make an assertion with Chai or is it even possible when I'm polling and waiting for stuff as my test? For example, in this case, I'm just trying to poll to make sure we ended up at the right URL within 30 seconds and I don't have an explicit assertion. I'm just assuming the test will fail, but it won't say why. If the best practice is to make an assertion here, how would I do it here or any time I'm using wait?
Here's an example of my code:
'create new account': function() {
return this.remote
// Hidden: populate all account details
.findByClassName('nextButton')
.click()
.end()
.then(pollUntil('return location.pathname === "/protected-page" ? true : null', [], 30000));
}
The pollUntil helper works by running an asynchronous script in the browser to check a condition, so it's not going to work across page loads (because the script disappears when a page loads). One way to poll the current remote URL would be to write a poller that would run as part of your functional test, something like (untested):
function pollUrl(remote, targetUrl, timeout) {
return function () {
var dfd = new Deferred();
var endTime = Number(new Date()) + timeout;
(function poll() {
remote.getCurrentUrl().then(function (url) {
if (url === targetUrl) {
dfd.resolve();
}
else if (Number(new Date()) < endTime) {
setTimeout(poll, 500);
}
else {
var error = new Error('timed out; final url is ' + url);
dfd.reject(error);
}
});
})();
return dfd.promise;
}
}
You could call it as:
.then(pollUrl(this.remote, '/protected-page', 30000))
When you're using something like pollUntil, there's no need (or place) to make an assertion. However, with your own polling function you could have it reject its promise with an informative error.

blueimp file upload. How to clean existing filelist

I have goggled a lot, but have not found a solution for my issue. The author of the widget references to the last answer of FAQ, but the FAQ does not have the answer or I cannot find it. I suppose it was updated since that time. Other fellows who faced the same issue and asked the same question just gone and did not provide any solution.
Anyway, in my case I have a table with button Pictures:
When a user clicks one of pictures button, modal dialog is shown. The user now can manage pictures for the chosen row. He can upload, delete pictures and so on. When the user opens the dialog for second row in the table he should see pictures for the second row only. It tells me that I have to clean the list of uploaded files every time user hits Pictures button to see the dialog. He will receive list of pictures which corresponds to chosen row from the server. Unfortunately, when I retrieve the list for the chosen row, the received files are added to the existing list.
Could you tell me how I can clean the list or reset the widget without removing files on the server side?
UPDATE I have used the following piece of code as a temporary solution.
jQuery.ajax({
url: "<YOUR URL HERE>",
dataType: 'json',
context: $('#fileupload')[0]
}).done(function (result) {
jQuery("#fileupload").find(".files").empty(); //this line solves the issue
jQuery(this).fileupload('option', 'done').call(this, null, { result: result });
});
Thank you.
i was also trying for one hour to get my upload work ;)
here is, how i solved this problem:
$('#html5FileInput').fileupload({
....
add: function (e, data) {
$.each(data.files, function (index, file) {
var newFileDiv = $(newfileDiv(file.name));
$('#fsUploadProgressHtml5').append(newFileDiv);
newFileDiv.find('a').bind('click', function (event) {
event.preventDefault();
var uploadFilesBox = $("#fsUploadProgressHtml5");
var remDiv = $(document.getElementById("fileDiv_" + event.data.filename));
removeFileFromArray(event.data.filename);
remDiv.remove();
data.files.length = 0;
...
});
data.context = newFileDiv;
});
...
)};
as you can see i create inside the add-event my file-dataset with 'newfileDiv(file.name)'. this creates a div with all information about the file (name, size, ...) and an ankor that exists for deleting the file from the list. on this ankor i bind a click-event in which i have the delete implementation.
hope this helps!
I know this isn't the most elegant solution, but I needed a very quick and dirty...so here's what I did (using jQuery).
//manually trigger the cancel button for all files...removes anything that isn't uploaded yet
$('.fileupload-buttonbar .cancel').first().trigger('click');
//check the checkbox that selects all files
if(!$('.fileupload-buttonbar .toggle').first().checked) {
$('.fileupload-buttonbar .toggle').first().trigger('click');
}
//manually trigger the delete button for all files
$('.fileupload-buttonbar .delete').first().trigger('click');
I know this isn't the best way. I know it isn't elegant...but it works for me and removes everything from the plugin.
If you have added file names or anything else from the plugin to any local arrays or objects, you'll need to clean those up manually (I have several handlers that fire on fileuploadadded, fileuploadsent, fileuploadcomplete, fileuploadfailed, and 'fileuploaddestroyed` events).
protected function get_file_objects($iteration_method = 'get_file_object') {
$upload_dir = $this->get_upload_path();
if (!is_dir($upload_dir)) {
return array();
}
return array_values(array_filter(array_map(
array($this, $iteration_method)
//scandir($upload_dir)
//File listing closed by Doss
)));
}
there is a scandir function responsible for listing the files. just uncomment it like i have done above and your problem is solved. it can be found in the UploadHandler.php file.

Removing record from Localstorage proxy in Sencha Touch 2

So my problem is this. I can remove a record from localstorage proxy just fine the first time. But if I do it again, it gives me an error, where everything in the Store is undefined, like it didnt exist anymore.
onTapRemoveKegelReminder: function(button) {
console.log(button.getData());
//Find and delete the button and the record
var store = Ext.getStore('KegelReminders');
store.load();
store.filter('button_id', button.getData());
var record = store.first();
console.log(record);
console.log(button.getData());
console.log('Remove count'+ store.getCount());
if (typeof record !== 'undefined'||record!=null ) {
store.remove(record);
store.sync();
console.log('removed record correctly')
this.trainingCount--;
var rmButton = this.getKegelExercises().down('#container-' + button.getData());
this.getKegelExercises().remove(rmButton);
}
But if I restart my application, and then remove again it works fine. I cant seems to remove more than once without having to restart the application.
FYI in case anyone else finds this, removing a record from a Store only removes it from that instance of the store, not from the storage mechanism (for example, localstorage). If you want to do that you have to use the erase method on the model object.
store.remove(record); // may not even be necessary
record.erase();
store.sync();

Dojo DnD Move Node Programmatically

I would like to know if there is a way to move the node programmatically in dojo Dnd? The reason is I would like to revert the changes to the drag and drop when the web service call triggered a failed save on the database. Here is what I have so far.
In my code, the node Id seems to be unrecognized by the dojo.dnd.Container.DelItem. I cannot just use the selected item on the target because this is a asynchronous webservice function callback. So the user may be selecting another node on the container when this is called.
function OnMoveWSComplete(strResult) {
var resultObj = eval('(' + strResult + ')');
var sourceContainer = eval('(' + objResult.sourceContainerId + ')');
var targetContainer = eval('(' + objResult.targetContainerId + ')');
var targetNodes = targetContainer.getAllNodes();
for (var iNode = 0; iNode < targetNodes.length; iNode++) {
var currId = getLastIdFromStr(targetNodes[iNode].id);
if (currId == resultObj.Id) {
var targetN = targetNodes[iNode];
var Name = targetNodes[iNode].childNodes[0].nodeValue;
targetContainer.delItem(targetNodes[iNode].id);
var origData = { Id: resultObj.Id, Name: Name };
sourceContainer.insertNodes(true, origData);
break;
}
}
}
EDIT: Solution (Thanks Eugene Lazutkin) [2009/11/30]:
/**
* Move one node from one container to the other
*/
function moveNode(nodeId, sourceContainer, targetContainer) {
var node = dojo.byId(nodeId);
// Save the data
var saveData = sourceContainer.map[nodeId].data;
// Do the move
sourceContainer.parent.removeChild(node);
targetContainer.parent.appendChild(node);
// Sync the DOM object → map
sourceContainer.sync();
targetContainer.sync();
// Restore data for recreation of data
targetContainer.map[nodeId].data = saveData;
}
It looks like you assume that delItem removes physical nodes. Take a look at the documentation — probably you want to move nodes between containers instead of deleting them from the map. One simple way to do that just to move DOM nodes between containers, and call sync() on both containers.
Addition: Here is a super-simple pseudocode-like example:
function move(node, source, target){
// normalize node and/or node id
node = dojo.byId(node);
// move it physically from one parent to another
// (from target to source) adding to the end
target.parent.appenChild(node);
// now it is moved from source to target
// let's synchronize both dojo.dnd.Source's
source.sync();
target.sync();
}
Or something along these lines should work. The important pieces:
Move node from one parent to another using any DOM operations you deem appropriate. I used appendChild(), but you can use insertBefore(), or anything else.
Synchronize both sources involved after the move.
Obviously it works if both sources use nodes of the same type and structure. If not, you should do something more complex, e.g., move everything you need emulating a real DnD move by publishing topics described in the documentation.
I have this function which moves selected nodes by button click:
source.forInItems(dojo.hitch(this, function(item, id, map) {
if (dojo.hasClass(id, "dojoDndItemAnchor")) {
target.onDrop(source, [ dojo.byId(id) ], false);
dojo.removeClass(id, "dojoDndItemAnchor");
}
}));
onDrop() is an overridable method, which is called upon item drop, and by default calls to method onDropExternal(source, nodes, copy).
I am doing the same thing right now. I was able to solve by doing the following.
Set the dnd/Source autoSync property to true
<div data-dojo-type="dojo.dnd.Source" accept="widget" class="source" data-dojo-props="autoSync: true">
After dragging it looses the dndtype so I had to re-add it using client side code. Also I remove the dojoDndItemAnchor class after drop.
$(node).removeClass('dojoDndItemAnchor').attr('dndtype', 'widget');

Dojo Tree Refresh

Using Dojo 1.3, after adding a child (i.e. folder or item) to a tree, is there a way to have it reflected immediately via refresh or some other method?
From the official Dojo manual
Updating a Tree
People often ask:
how do I update a tree (adding or
deleting items?)
You can't update the
tree directly, but rather you need to
update the model. Usually the model is
connected to a data store and in that
case you need to update the data
store. Thus, you need to use a data
store that allows updates (through
it's official API), like
dojo.data.ItemFileWriteStore.
how do I refresh a Tree from the
store?
This isn't supported. The store
needs to notify the tree of any
changes to the data. Currently this is
really only supported (out of the box)
by dojo.data.ItemFileWriteStore, as
setting up a client-server dojo.data
source where the server notifies the
client whenever the data has changed
is quite complicated, and beyond the
scope of dojo, which is a client-only
solution.
Say, if your model has the query `{type:'continent'} - meaning any items with this property are top-level-items, then the following will model extension will monitor changes and refresh the tree's view
var dataStore = new ItemFileWriteStore( { ... });
new Tree({
store: dataStore,
model: new ForestModel({
onNewItem: function(item, parentInfo){
if(this.store.getValue(item, 'type') == 'continent'){
this._requeryTop();
}
this.inherited(arguments);
}
}
});
This should in turn call childrenChanged in the tree and update it every time a new item is added.
See model reference
As an addition, if the item added is not a toplevel item, an immediate update should be doable with this statement. parent is the treenode which has had an item added to its children.
tree._collapseNode(parent);
parent.state = 'UNCHECKED';
tree._expandNode(parent);
A more or less 'standard' refresh of the tree can be achieved by following. Reason for it not being added to base implementation, i think is because it will break the linkage with DnD features on a tree
dojo.declare("My.Tree", [dijit.Tree], {
// Close the store? (So that the store will do a new fetch()).
reloadStoreOnRefresh : true,
update: function() {
this.model.store.clearOnClose = this.reloadStoreOnRefresh;
this.model.store.close();
// Completely delete every node from the dijit.Tree
delete this._itemNodesMap;
this._itemNodesMap = {};
this.rootNode.state = "UNCHECKED";
delete this.model.root.children;
this.model.root.children = null;
// Destroy the widget
this.rootNode.destroyRecursive();
// Recreate the model, (with the model again)
this.model.constructor(this.model)
// Rebuild the tree
this.postMixInProperties();
this._load();
}
}
);
I've solved this WITHOUT needing the refresh.
_refreshNodeMapping: function (newNodeData) {
if(!this._itemNodesMap[newNodeData.identity]) return;
var nodeMapToRefresh = this._itemNodesMap[newNodeData.identity][0].item;
var domNode = this._itemNodesMap[newNodeData.identity][0].domNode;
//For every updated value, reset the old ones
for(var val in newNodeData)
{
nodeMapToRefresh[val] = newNodeData[val];
if(val == 'label')
{
domNode.innerHTML = newNodeData[val];
}
}
}