I am playing with vis.js and have worked out how to get an array of all the currently selected nodes.
var TempNodes = network.getSelectedNodes();
My problem is that the getSelectedNodes() array is returned with all the nodes id's in numeric order from lowest to highest...There doesn't seem to be a way to tell what the last selected node id was or what the first selected node id was. I can only tell which node id's were selected.
Does anyone know a way to be able to find out from the getSelectedNodes array, what was the first or last selected node id ?
Using the concepts tucuxi put forward, I did come up with some working code to achieve this. Although tucuxi's code did not work 'straight out of the box' his idea was sound and he deserves credit for it.
Here is the code that eventually worked for me
var PreviouslySelectedNodes = [];
var SelectedNodesInOrder = [];
network.on('select', function (properties) {
// itterate through each visjs selected nodes and see if any value is not present in our current ordered list
// If it's not already present, push it to the top of our ordered list
var SelectedNodeIDs = network.getSelection().nodes // First get all the visjs selected nodes
// If only one Node is currently selected, then empty the SelectedNodesInOrder array ready for a new set
if(SelectedNodeIDs.length == 1){
SelectedNodesInOrder = [];
}
// Cycle through all current selected visjs nodes
for(var t = 0; t <= SelectedNodeIDs.length; t++){
// cycle through all currently selected ordered nodes in our ordered array
var found = false; flag the default as this node not already in our ordered list
for(var y = 0; y <= SelectedNodesInOrder.length; y++){
if(SelectedNodesInOrder[y] == SelectedNodeIDs[t]){ // This node already exists in our ordered list
found = true;
}
}
// If the current visjs selected node doesn't already exist in the SelectedNodesInOrder, then add it
if(found === false){
SelectedNodesInOrder.push(SelectedNodeIDs[t]);
}
}
console.log(SelectedNodesInOrder); // test by dumping our ordered array list to the console
});
The documentation specifically states that getSelection()
Returns an array with the ids of the selected nodes. Returns an empty array if no nodes are selected. The selections are not ordered.
Since at present, the way to select multiple nodes is to long-press them, you can keep selection-order yourself, by listening to the selection event:
var mySelectionOrder = [];
var previouslySelected = {};
network.on('select', function(p) {
var selected = {};
// add newly-selected nodes at end of mySelectionOrder
p.nodes.forEach(function(n) {
if ( ! previouslySelected[n]) { mySelectionOrder.push(n); }
selected[n] = true;
});
// remove newly-unselected entries from mySelectionOrder
mySelectionOrder = mySelectionOrder.filter(
function(e, i, a) { return selected[e]; });
// prepare for next run
previouslySelected = selected;
// output to console to make sure it works
console.log("Selection updated", mySelectionOrder);
});
(Edited to fix JS code; tested to work with this example)
Related
I am trying to make a dynamic dropdown list and Section Navigation in Google Form. However, my script can auto delete the choice when the quota has been met, the choice can’t navigate to the related page for other selections.
I am planning a health check event for my hospital. It needs to reserve by timeslot and date due to the crow control policy. The links below are my Google Spreadsheet for the form and my daft Google Form of the function.
https://forms.gle/ZV9Djni8hyQGdAd86
https://docs.google.com/spreadsheets/d/1F1dpGCTSlpEOUMh5txsZouhx784JmJvh66IsiGfDTtg/edit?usp=sharing
Reference:
How to set the go to sections on a Google Forms question using app script
https://www.pbainbridge.co.uk/2019/04/dynamically-remove-google-form-options.html
function appointmentSlots() {
var form = FormApp.openById("1VqFBKBD_-iKYk_3Ze40j2tvRIi093-alaoCDsXpFi8k");
var ss = SpreadsheetApp.getActiveSpreadsheet();
var date1timelist = form.getItemById("2101588132").asListItem();
var optionsSheet = ss.getSheetByName('Date Options');
var dateoptions = optionsSheet.getRange('A2:A3').getValues();
var dateleft = optionsSheet.getRange('C2:C3').getValues();
var day1sheet = ss.getSheetByName('9/3');
var day1timeoptions = day1sheet.getRange('A2:A4').getValues();
var day1left = day1sheet.getRange('C2:C4').getValues();
var formFieldsArray = [
["9/3", 2061926149],
["10/3", 1632977105]
];
for(var h = 2; h < formFieldsArray.length; h++) {
var datelist = form.getItemById(formFieldsArray[h][2]).asListItem();
var avaibledateoptions = [];
var sectionday1timeslots = form.getItemById("2101588132").asPageBreakItem();
var sectionday2timeslots = form.getItemById("1630116063").asPageBreakItem();
var datechoice = datelist.getChoices();
var optionsDataLength = dateoptions.length;
for (var i=0; i<optionsDataLength; i++) {
var choice = dateoptions[i][0];
var left = dateleft[i][0];
if ((choice != '') && (left > 0) == formFieldsArray[h][2]) {
if (formFieldsArray[h]= "9/3") {
datechoice.push(datelist.createChoice(avaibledateoptions,sectionday1timeslots));
}
else {
datechoice.push(datelist.createChoice(avaibledateoptions,sectionday2timeslots));
datelist.setChoices(avaibledateoptions);
}
}
}
}
var day1avaibledateoptions = [];
var optionsday1Length = day1timeoptions.length;
for (var i=0; i<optionsday1Length; i++) {
var day1timechoice = day1timeoptions[i][0];
var day1timeleft = day1left[i][0];
if ((day1timechoice != '') && (day1timeleft > 0)) {
day1avaibledateoptions.push(day1timechoice);
}
}
date1timelist.setChoiceValues(day1avaibledateoptions)
}
//etc for day2 timeslots choice and day3 timeslots
}
}
}
In order to modify your form depending on the changing cell values in your Spreadsheet (caused by new form submissions) you will need to set up an installable onChange trigger that will basically run your function when a change on your Spreadsheet is done (like one coming from a form submission). To create such a trigger, please access your trigger pannel and then click on Create trigger and select as the event type onChange assigning it to the function you will be using to create/delete the form items.
Once a user submits a new form and you do certain calculations on your Spreadsheet to determine how many slots are free for that time slot, you can take the value of the cell that tells you how many free appointments are free for that time and if that number is 0 you can proceed to delete that question element using the method deleteItem().
If you eventually end up resetting the form (because your time slot is free again or someone cancels the meeting), you can undo this by creating back the element.
The following piece of code is a basic example on how to delete and create form items based on the changes of a Spreadsheet cell. It has self explanatory comments:
function onChange() {
// Get the different sheets where you have all your left places in your time slots
var sheet = SpreadsheetApp.getActive().getSheetByName('A');
// Get your form
var form = FormApp.openById('FORMID');
// Here you would get each element that might depend on whether there are any
// appointments left or not
var element = form.getItems()[2];
// Get the cell value that tells you if the time slot is already full (full=0)
var value = sheet.getRange('C2').getValue();
// If the value is 0 it means that this time slot is all completed and nobody
// should be able to select it again
if (value == 0) {
// delete this item
form.deleteItem(element);
// if it is not full yet, it might be because your reset the time slot and therefore
// the element does not exist any more
} else {
// if the element exists dont do anything but if it doesnt and there are available
// apointments create it again
if (!element) {
form.addMultipleChoiceItem().setTitle('B').setChoiceValues(['Cats', 'Dogs']);
}
}
}
If you want to remove a choice option rather than an Item, you can look for the item, get all the choices as an array and then remove the choice you don't want any more from this array. Finally, you can update the item with your updated options with setChoices(). Here is a code example on how to achieve this:
function myFunction() {
// This is an example where I only have a single multiple choice item
var choiceItem = FormApp.getActiveForm().getItems(FormApp.ItemType.MULTIPLE_CHOICE)[0].asMultipleChoiceItem();
// Get current choices array
var choices = choiceItem.getChoices();
// Get choice you want to delete, this would be your times or dates obtained from
// the cell values
var choiceToBeRemoved = "A";
// remove choice from array
choices = choices.filter(function(choice){return choice.getValue() !== choiceToBeRemoved});
// Set updated choices
choiceItem.setChoices(choices);
}
References
setChoices
Javascript filter()
I'm new to coding, and I need to display past search values from an input field using localstorage. The only way I can think of is by using one object key with an array of stored values from an on click event. Problem is, I can only get one position to appear as a value, with each value generated replacing the last. I've tried for loops and can't seem to get it to work. This is the code I have so far:
$('.search-city').on('click', function(e){
e.preventDefault();
var textArr = [];
var text = $(".form-control").val();
textArr.push(text);
localStorage.setItem("value1", textArr);
});
$('.search-city').on('click', function(e){
e.preventDefault();
var search = localStorage.getItem("value1")
This would work:
$('.search-city').on('click', function(e){
e.preventDefault();
// get the value from local storage
var localValue = localStorage.getItem('value1');
// if we had a value, parse it back to an array, if we dont, create an empty array
var textArr = localValue ? JSON.parse(localValue) : [];
// get the text from the search input, dont use "form-control"
// you're likely to have several of those on the page
// give the element a custom class like "search-input" and use that (id would be even better)
var text = $('.search-input').val();
// add the text to the array
text = trim(text);
if (text) {
textArr.push(text);
}
// enforce a size limit here by removing the 0 index item if the count has grown too large
var maxAllowed = 10;
while (textArr.length > maxAllowed) {
textArr.shift();
}
// localstorage can only hold simple strings so we'll JSON stringify our object and store that
localValue = JSON.stringify(textArr);
localStorage.setItem("value1", localValue);
});
I am writing a script that allows organisation of records and data.
However a problem right now is I use an array to store a large amount of information and at a point in my script this array is searched for a matching id and updated. The data search begins to get quite slow when the array size is more than 200 elements.
Is there any way to more efficiently search the array?
I am currently using find.(function(a){return x == y};);
Each element in my array is also a tuple.
Sample code:
onPipelineChanged: function(component, event, helper)
{
var titleid = event.getParam("titleid");
var title = event.getParam("title");
var maxSeats = event.getParam("maxSeats");
var seatsInUse = event.getParam("seatsInUse");
var item = event.getParam("item"); //This is the item that was moved.
var allGuestsList = component.get("v.allItems");
//Searches guest list for matching guest of who got moved.
var actualGuest = allGuestsList.find(function(moving){return moving.id == item.id;}); //***REALLY INEFFICIENT***
I upload an SVG file to a canvas using FabricJS with the function
fabric.loadSVGFromURL (url, function(objects, options){
group = fabric.util.groupSVGElements(objects, options);
canvas.add(group).centerObject(group).renderAll();
});
This works perfectly. However the next step I want do is to ungroup the recently added group. The reason why I need to ungroup is that I want to be able to select the group's child elements by clicking on them since there is no access to these elements if they are grouped.
I found a snippet to perform an ungroup however when I do it with the group created width groupSVGElements the elements lose their original position scrambling the whole svg that I loaded.
Does anyone knows how to ungroup a loaded SVG and still keep the original positions of the elements?
You can still access each of the element using perPixelTargetFind
When set to true, objects are "found" on canvas on per-pixel basis rather than according to
bounding box.
I'm looking for the same solution. Did you find an answer so far?
Looking at the structure of an SVG element, I would image it should be possible to write a recursive method, which gives the children, the properties of the group and places them one level up. If you keep doing this, you should end up with all groups exploded and all properties intact (which are inherited otherwise).
Looking at SVG-EDIT, there is a function which should do this:
Function: ungroupSelectedElement
// Unwraps all the elements in a selected group (g) element. This requires
// significant recalculations to apply group's transforms, etc to its children
this.ungroupSelectedElement = function() {
var g = selectedElements[0];
if (!g) {
return;
}
if ($(g).data('gsvg') || $(g).data('symbol')) {
// Is svg, so actually convert to group
convertToGroup(g);
return;
}
if (g.tagName === 'use') {
// Somehow doesn't have data set, so retrieve
var symbol = svgedit.utilities.getElem(getHref(g).substr(1));
$(g).data('symbol', symbol).data('ref', symbol);
convertToGroup(g);
return;
}
var parents_a = $(g).parents('a');
if (parents_a.length) {
g = parents_a[0];
}
// Look for parent "a"
if (g.tagName === 'g' || g.tagName === 'a') {
var batchCmd = new svgedit.history.BatchCommand('Ungroup Elements');
var cmd = pushGroupProperties(g, true);
if (cmd) {batchCmd.addSubCommand(cmd);}
var parent = g.parentNode;
var anchor = g.nextSibling;
var children = new Array(g.childNodes.length);
var i = 0;
while (g.firstChild) {
var elem = g.firstChild;
var oldNextSibling = elem.nextSibling;
var oldParent = elem.parentNode;
// Remove child title elements
if (elem.tagName === 'title') {
var nextSibling = elem.nextSibling;
batchCmd.addSubCommand(new svgedit.history.RemoveElementCommand(elem, nextSibling, oldParent));
oldParent.removeChild(elem);
continue;
}
children[i++] = elem = parent.insertBefore(elem, anchor);
batchCmd.addSubCommand(new svgedit.history.MoveElementCommand(elem, oldNextSibling, oldParent));
}
// remove the group from the selection
clearSelection();
// delete the group element (but make undo-able)
var gNextSibling = g.nextSibling;
g = parent.removeChild(g);
batchCmd.addSubCommand(new svgedit.history.RemoveElementCommand(g, gNextSibling, parent));
if (!batchCmd.isEmpty()) {addCommandToHistory(batchCmd);}
// update selection
addToSelection(children);
}
};
See also:
https://code.google.com/p/svg-edit/source/browse/trunk/editor/svgcanvas.js
I have a series of editable lists which, on a press of a button should be transformed into some sort of data structure. When it has been turned into some sort of data I need to add duplicates together.
Example:
200g banana
100g apple
200g apple
Should be turned into a data list of some sort and should in the end look like this:
200g banana
300g apple
Here's my attempt:
//button click event
$(".calculate").bind("click", function(e)
{
//get the correct parent of the button
var parent = $(this).closest("#calc");
//get relevant data
parent.find(".options").each(function(index, element)
{
var opt1 = $(this).children(".opt1").children("input").val(); //weight
var opt2 = $(this).children(".opt2").children("input").val(); //ingredient
});
});
Basically I click the button and the above script finds all the relevant data.
How can I turn this into a multidimensional array or a list of objects I can search for duplicates in?
When I try to make a dynamic object it seems to fail and when I make a multidimensional array to search in I get blocked by inArray's inability to search through them.
Problem recap:
I am able to get the user data no problem. Turning it into a list and adding together duplicates is the problem.
I will suggest you to have a global object that will contain the summary, this will look like this:
$(".calculate").bind("click", function(e)
{
var fruits = {};
//get the correct parent of the button
var parent = $(this).closest("#calc");
//get relevant data
parent.find(".options").each(function(index, element)
{
var opt1 = $(this).children(".opt1").children("input").val(); //weight
var opt2 = $(this).children(".opt2").children("input").val(); //ingredient
// here is my code
if(fruits[opt2] == undefined) {
fruits[opt2] = opt1;
} else {
// assuming that opt1 is an integer
fruits[opt2] += opt1;
}
});
// use fruits variable here
});
Here's another variant, which also does some simple parsing in case you have 100g as input, versus 100. Also, the data structure gets reinitialized every time, so everything does not get doubled on every click.
$(".calculate").bind("click", function(e)
{
//get the correct parent of the button
var parent = $(this).closest("#calc");
var ingredients = {};
var extractWeight = function (input) {
// you can add other logic here
// to parse stuff like "1kg" or "3mg"
// this assumes that everything is
// in grams and returns just the numeric
// value
return parseInt(input.substring(0, input.length - 1));
}
//get relevant data
parent.find(".options").each(function(index, element)
{
var opt1 = $(this).children(".opt1").children("input").val(); //weight
var opt2 = $(this).children(".opt2").children("input").val(); //ingredient
// initialize to 0 if not already initialized
ingredients[opt2] = ingredients[opt2] ? ingredients[opt2] : 0;
ingredients[opt2] += extractWeight(opt1);
});
});
Here are some tips:
{} is called an object literal and is used to create a new empty object
object members can be accessed dynamically through the [] notation (i.e. if x === "name" then o[x] === o.name)
variables are visible inside functions that are at the same level or deeper in the scope - like in my example I use ingredients in the each function.
arrays in JavaScript only support numeric keys, so you won't have stuff like PHP's "associative arrays". Objects fill this gap in JS.
Here is a jsFiddle that does what you're looking for :) http://jsfiddle.net/LD9TY/
It has two inputs, one for the item name and the other for the amount. When you click add, it checks an object to see if the item was already added. If so, it increments the amount for that item based on your input. If not, it adds that item with the amount you specified. It then goes and builds a ul with all the items in your "store".
Note that this is a quick and dirty example, so there is no type checking or validation going on :)