I have the following data structure (sample):
Folder1
- Folder2
- Snip1
- Snip2
- Folder3
- Snip3
- Snip4
- Folder4
- Snip5
There is no limit on nesting of folders inside one another
I need to allow the user to insert a particular snip whenever he/she right-clicks on a textarea. Now, I can either flatten the above data to:
- Snip1
- Snip2
- Snip3
- Snip4
- Snip5
to simply create context menu entries. But, I want better user experience so I will prefer something like:
to simulate the folder structure in the actual data.
Question: Is it possible to generate such type of context menu items' structure though Chrome extensions?
I have looked at the docs, but found nothing useful.
UPDATE: For anyone interested in the code, here's it:
// this is the top most folder
this.createCtxMenuEntry = function(parentId){
this.list.forEach(function(object, index){
var id = chrome.contextMenus.create({
contexts: ["editable"],
id: // generateUniqueIDYourself
title: object.name,
parentId: parentId
});
if(object is folder) object.createCtxMenuEntry(id);
});
};
The trick is to use the parentId property.
yes, it's possible to create dynamically (based on user data and changes) nested context menu with chrome extension
I created exactly the same thing with my V7 Notes extension (inserting notes in text fields)
example:
first you need to create main item and then recursively (I guess you have folder structure) create sub-folders appended to that main item
var OriginalArrayData = [....]; //or whatever you have
chrome.contextMenus.create({
title : "MAIN title",
id: "main_ID", //call it whatever you like, but it needs to be unique
type : "normal",
contexts : ["editable"],
},function() {
buildTree(OriginalArrayData, 'main_ID'); //pass original array and main_ID in first call
});
function buildTree(a, b) {
for (var i=0, l=a.length; i<l; i++) { //loop trough passed data
var notId = a[i].id, //create random unique ID for new items, I'm using id's from my notes
notText = a[i].text; //create item title, I'm using text from my notes
chrome.contextMenus.create({ //create CTX item
id: notId,//for ID use previously created
parentId: b,//for parent ID use second passed argument in function
title: notText,//for title use previously creted text (or whatever)
type: "normal",
contexts: ["editable"]
});
if (a[i].list) buildTree(a[i].list, notId);//if folder, recursively call the same function
}
}
on the last line, whenever your loop runs into a folder, you need to call your buildTree function, but this time you need to pass data/array from within that folder, and that folder ID so that it can be used as parent ID for further children
When function finishes traversing trough sub folders, it returns one step UP where it was entering those sub-loops
So, to summ up:
- create main item
- in callback create recursive function to loop all your data and create/append those new items to main item
- if you have folder, that folder now becomes main item and all it's children will be append to it...and so on
- this way context menu will follow folders structure of your data as deep as they are
I think this is the easiest and lightest way to create dynamic folder structure on context menus
When some of the data changes, you need to clean context menu and create it all over again... (or update it if you know which to target)
Related
I am building an html page with model query functionality starting from the viewer.
I have implemented some methods including viewer.getProperties () and viewer.getBulkProperties (). Working on these themes I realized that it would be very useful to create instances of the panels (modelstructure, properties etc ...) in elements external to the viewer (not in the docking panels), maintaining the functionality and if possible customizing them (for example showing the elements not -collapsed).
First question: is it possible to do this? Second question: suggestions on a general method or tutorial for this theme?
For the Q1, it's possible to do that, appending the container of the ModelStructurePanel to a div outside the viewer container, but it's not recommended. It would take extra effort to fix CSS of the ModelStructurePanel after moving it out from viewer container.
For the Q2, you could take advantage of the jstree.js to make a three like UI, and for rebuilding model hierarchy data as the native ModelStructurePanel does, here is a code snippet for you. You would have to modify it to match jstree's data requirement.
function buildModelTree( model ) {
//builds model tree recursively
function _buildModelTreeRec( node ) {
it.enumNodeChildren( node.dbId, function(childId) {
node.children = node.children || [];
var childNode = {
dbId: childId,
name: it.getNodeName( childId )
};
node.children.push( childNode );
_buildModelTreeRec( childNode );
});
}
//get model instance tree and root component
var it = model.getData().instanceTree;
var rootId = it.getRootId();
var rootNode = {
dbId: rootId,
name: it.getNodeName( rootId )
};
_buildModelTreeRec( rootNode );
return rootNode;
}
var root = buildModelTree( viewer.model );
Then you will need to bind those events to change tree UI's look in some particular situations:
Autodesk.Viewing.SELECTION_CHANGED_EVENT: To make tree nodes be selected.
Autodesk.Viewing.ISOLATE_EVENT:
Autodesk.Viewing.HIDE_EVENT: To change tree node look, make tree nodes' text color gray if this node is invisible.
Autodesk.Viewing.SHOW_EVENT: To change selected three node's text color from gray to black
Autodesk.Viewing.ISOLATE_EVENT: a combination of the HIDE_EVENT and SHOW_EVENT.
And bind select_node.jstree event to isolate, fit to view the viewer objects according to the selected three nodes.
Isolate: Viewer3D#isolate( dbIds )
Fit to view: Viewer3D#fitToView( dbIds )
I'll try to be as brief as possible as my situation is unique and I am a fairly new programmer.
TL:DR - Cannot get the row to update, table does not remember initial values when editing, cell.setValue() creates recursive loop, cell.getRow().update() seems to do nothing, row does not resize in the event of editing working (still does not hold on to previous values)), unsure if mutator or cellEdit() callback are potential solutions.
I have JSON data populating my table from an AJAX call to a Java backend. One of the columns can either be one item, a list of multiple items, or none at all. I am using a custom formatter to populate that cell with the list of items, followed by icons to remove those items...
Some text here X
Some other text X
And some more X
When clicking the X icon, it should remove the item, and when clicking the text, it will drop down a list to choose another option.
Some text here X
Some other text X
Added another line X <----removed the previous item and selected a new
The problem I am having is that the table does not continuously hold onto previous values nor concatenates them with new selections. I have written some code to take care of the concatenation issue, but it still does not save that info within the table. I have tried to use cell.setValue() but this creates a recursive loop as all my code is within the custom formatter. I have also tried cell.getRow().update() but this seems to do nothing.
Also, clicking into the drop down and clicking away without selecting a value will still send the previous value as though it was clicked again. I was able to get it to function once, but it only changed the most recent values and did not include the original ones and it did not resize the row to be smaller.
I am unsure if a custom mutator would solve my issue, or even how to get the mutator to interact with the formatter to update the cell every time a change has been made. I also have a cellEdited: function(){} callback that is going to be designed to take the entire row of information as a JSON object to save it within a DAO and considered that I might be able to solve the issue there?
Here is my code for that column. This currently will load the page with the initial design yet does not work with editing. "cell.setValue()" and "cell.getRow().update()" are currently not included because they weren't working properly...
title: "Groups",
field: "associatedGroups",
variableHeight: true,
headerFilter: true,
headerFilterPlaceholder: "Search by Group...",
formatter: function (cell, formatterParams, onRendered) {
var data = [];
var newValue = cell.getValue();
var oldValue = cell.getOldValue();
var newValueIsArray = $.isArray(newValue);
var oldValueIsArray = $.isArray(oldValue);
if (oldValue !== null && newValue !== null) {
if (!oldValueIsArray && newValueIsArray) {
data = data.concat(newValue);
data.unshift(oldValue);
} else if (oldValueIsArray && !newValueIsArray) {
data = data.concat(oldValue);
data.push(newValue);
} else {
data.push(oldValue);
data.push(newValue);
}
} else {
data = newValue;
}
<!-------------------Value of "data" used here to determine visual layout----------------->
},
editor: "select",
responsive:
0,
editorParams:
{
values: groupNames
}
,
sorter: "string",
width:
212
},
Lets say I'm working with a 3D file which is the combination of one Architectural model and one Structural model.
The instance tree or Model Browser looks like this
root/
Arch/
Level 01/
Level 02/
...
Str/
Level 01/
Level 02/
...
I want to display only the Level 01.
So I:
Followed the steps in the Viewer tutorial
Add an event listener to both Autodesk.Viewing.GEOMETRY_LOADED_EVENT & Autodesk.Viewing.OBJECT_TREE_CREATED_EVENT
When the 2 are fired, I use the code in this article to display only the Level 01 without ghosting.
I have 2 problem with this approach
I have to wait until the entire model is loaded before I can filter the level
After filtering the level, if I click on Model Browser, I can still see the entire model structure (but with everything as hidden except Level 01). How can I set the instance tree to only have what's below?
root/
Arch/
Level 01/
Str/
Level 01/
EDIT
At what point am I supposed to override the shouldInclude() function?
I've tried this and put a breakpoint but it seems it never gets called... I also tried to move it around but in vain.
const start = Date.now();
Autodesk.Viewing.UI.ModelStructurePanel.shouldInclude = (node) => {
Logger.log(node);
return true;
};
Autodesk.Viewing.Initializer(options, () => {
Logger.log(`Viewer initialized in ${Date.now() - start}ms`);
const config = {};
// prettier-ignore
Autodesk.Viewing.theExtensionManager.registerExtension('MyAwesomeExtension', MyAwesomeExtension);
viewerApp = new Autodesk.Viewing.ViewingApplication('MyViewerDiv');
viewerApp.registerViewer(viewerApp.k3D, Autodesk.Viewing.Private.GuiViewer3D, config);
loadDocumentStart = Date.now();
// prettier-ignore
viewerApp.loadDocument(documentId, onDocumentLoadSuccess, onDocumentLoadFailure);
});
Regarding #1: the object tree is stored in the file's internal database which - for performance reasons - is only loaded after the actual geometry.
Regarding #2: you can subclass the ModelStructurePanel class and add your own behavior, for example, by overriding the ModelStructurePanel#shouldInclude method.
Since I wasn't able to understand how to use ModelStructurePanel, I overrode Autodesk.Viewing.ViewingApplication.selectItem to only modify the options which are either passed to loadDocumentNode or startWithDocumentNode as below:
const options = {
ids: leafIDs.length > 0 ? leafIDs : null, // changed this line
acmSessionId: this.myDocument.acmSessionId,
loadOptions,
useConsolidation: this.options.useConsolidation,
consolidationMemoryLimit: this.options.consolidationMemoryLimit || 100 * 1024 * 1024, // 100 MB
};
With leafIDs being an array of objectIDs to display. I was able to build it by:
querying the ModelDerivativeAPI using GET :urn/metadata/:guid
going through the tree to find the ids which I am interested in.
There's probably a more elegant way to do this but that's the best I could do so far.
I hope someone can help me with this, It's a strange question maybe as I didn't find an answer online.
I call the database and retrieve a list (in json) of items.
Then in angularjs,I render this list by extracting relevant pieces of data(name,age,etc) and show it properly in a table as a list of rows.
I have then an edit button that takes me to another page where I want to put a dropdown list.
What I want to know if is possible to add to that dropdown list the rendered list I previously created in my previous page.
is it possible to save the previously rendered list in a variable and then use that variable in the dropdown?
thank you
You could store the list within a controller and make this data availablte to this dropdown, I think.
Instead of trying to query for the list, add the list to the template, get the list from the template and render somewhere else, I'd suggest query for the list, save the list in a service , and then when you want to use that list again, get it from the service. Something like:
service:
var services = angular.module('services');
services.factory('getListService',['$http',function($http){
var getListOfStuff = function(){
//call to database
return //your json
};
var extractNameAgeEtc = function(){
var myListOfStuff = //get list of stuff from $http or database
var myListOfNameAgeEtc = //make a list of tuples or {name,age,etc} objects
return myListOfNameAgeEtc;
};
return {
extractNameAgeEtc : extractNameAgeEtc
};
}]);
controllers:
angular.module('controllers',['services']);
var controllersModule = angular.module('controllers');
controllersModule.controller('tableRenderController',['getListService','$scope',function(getListService,$scope){
//use this with your table rendering template, probably with ng-repeat
$scope.MyTableValue = getListService.extractNameAgeEtc();
}]);
controllersModule.controller('dropdownRenderController',['getListService','$scope',function(getListService,$scope){
//use this with your dropdown rendering template, probably with ng-repeat
$scope.MyDropDownValue = getListService.extractNameAgeEtc();
}]);
I am using the SSRS 2008r API to create and manage SSRS from a webform application. When creating a folder I see where I can add a folder name as well as specify additional meta data (custom properties) that can be a part of the folder. My question is how do I populate additional fields in the catalog database via the api. When I look at the CreateFolder method the only properties I can add at the insert are folder name, path, and custom properties:
rs.CreateFolder(folderName, "/", props); // foldername is a string passed in from the form
However I would also like to set at this time the description, and hidden value.
I'd appreciate any suggestions on how this is accomplished. Every example I have seen within MSDN only shows setting the folder name, path, and custom properties.
thanks in advance
Set the item properties (Description and Hidden) by initializing a Property class for each. Never done it before, but I'm guessing it would look something like this (assuming C#):
...
// description property
Property description = new Property();
description.Name = "Description";
description.Value = "Your description here.";
// hidden property
Property hidden = new Property();
hidden.Name = "Hidden";
hidden.Value = "True"; // not sure on value here, may be True/False, Yes/No
// build properties array
props[0] = description;
props[1] = hidden;
// create folder
rs.CreateFolder(folderName, "/", props); // foldername is a string passed in from the form