How to apply category to document in Alfresco with javascript?
tried like this:
document.properties["cm:categories"] = "testCat";
UPDATE:
like this I am able to get "root categories" but not "sub categories":
var rootCats = classification.getRootCategories("cm:generalclassifiable");
document. properties["cm:categories"] = rootCats[3];
this doesnt work:
document. properties["cm:categories"] = rootCats[3][1];
I found it out, if someone need it this is the way how it cana be done
get all root categories [audio, video, image, ...]
var rootCats = classification.getRootCategories("cm:generalclassifiable");
get subcategories from desired root category -> sub category from "video" [horror, comedy, ...]
var singleCat = rootCats[1].subCategories;
apply category to document -> apply "horror" to document
doc. properties["cm:categories"] = singleCat[0]; //app
save it
doc.save();
First make sure that the document has the aspect classifiable (ie having the cm:generalClassifiable aspect).
Then look up the category you want to add ie:
UPDATE: an example query to find a node of type category of a certain name:
Search for your category like this for example, you could add a PATH clause as well to be sure your in the right hierarchy.
var nodes = var categories= search.luceneSearch('+TYPE:"http://www.alfresco.org/model/content/1.0}category" +#name:testCat');
then get the element out of the returned array...
Get the existing categories for the node:
categories= document.properties["cm:categories"];
Push the new category onto the array:
categories.push(categoryToAdd);
Assign this to the document:
document.properties["cm:categories"] = categories;
document.save();
We had a similar requirement, and although slightly different to OP question, it is close enough to warrant inclusion here we feel.
We occasionally have a need to bulk apply categories. ie:new business acquired, migrate all their docs in, add categories to match existing content.
Adding categories to large numbers of documents is tedious in the interface. It needs to enable the aspect (classifiable), then edit properties to select the categories required. For every document individually. Worse, the add aspect isn't available for documents from the explorer view in share, so entering the preview is required. For large docs, entering the preview is itself a few more seconds delay.
So we built a simple script that applies the categories of the parent folder to everything placed under it. Then we only need to set the categories of that folder and start moving content through it (ie:move content in to obtain the category/ies, then move back to where it came, but now all have aspect and category/ies applied).
Actually we have 2 at the moment:
The first folder/rule overrides whatever categories the objects had and
replaces them with just those from the parent folder - we name this folder BulkCategorise
The 2nd is cumulative, so it keeps existing categories and only
adds those from the parent folder, which we name AccumulateCategories
We will create a third to selectively remove them when time permits.
A couple of notes:
adding the classifiable aspect is not actually required to be done
manually or in code, it "Just Works" - at least in 5.1.
this may not be the best way to do this
accumulating a category that is already applied doesn't appear to cause any issues, they appear to coalesce/merge automatically
To use these scripts, create 2 folders and apply a rule to each to run the associated script.
The 1st script completely replaces the categories of the object with those of the parent folder and is trivially simple [Set the #name and SITE to match your installation]:
var inheritFromDir = search.luceneSearch('TYPE:"http://www.alfresco.org/model/content/1.0}folder" +#name:BulkCategorise +SITE:YOUR_SITE_NAME');
// We need to deref the array of noderefs result using inheritFromDir[0]
document.properties["cm:categories"] = inheritFromDir[0].properties["cm:categories"];
document.save();
The 2nd script is a bit more involved - we left the logging code in (commented out) for an example to see how to do that, because it took some time to figure out also (and is an extremely useful feature). As before update the #name and SITE on the first line to match your system.
var inheritFromDir = search.luceneSearch('TYPE:"http://www.alfresco.org/model/content/1.0}folder" +#name:AccumulateCategories +SITE:YOUR_SITE_NAME');
// We need to deref the single array result using inheritFromDir[0] to get categories from topmost parent (folder) node
var parentCatArray = inheritFromDir[0].properties["cm:categories"];
//and any existing categories of the doc, or null if none exist
var thisCatArray = document.properties["cm:categories"];
var thisLen = 0;
if (thisCatArray != null) {
thisLen = thisCatArray.length;
} else {
thisCatArray = new Array();
}
// Some logging to find out what is going on...
//var logFile = space.childByNamePath("log.txt");
// If the log file does not already exist, create it in current folder.
//if (logFile == null) {
// logFile = space.createFile("log.txt");
//}
//if (logFile != null) {
// logFile.content += new Date().toGMTString() + "\tRun started. Length of existing array is: " + thisLen + "\r\n";
// logFile.content += new Date().toGMTString() + "\tFound parent node categories:\r\n";
for (var i=0; i < parentCatArray.length; i++)
{
thisCatArray[thisLen]=parentCatArray[i];
thisLen += 1;
}
//}
// Push the new array of categories to the document
thisCatArray.push(document);
// apply and save the doc
document.properties["cm:categories"] = thisCatArray;
document.save();
Et Voila! In a similar way we have implemented BulkTag and BulkAccumulateTags. Now adding arbitrary categories is as simple as applying them to the folder and moving the content through with drag and drop.
Related
Update: This is a better way of asking the following question.
Is there an Id like attribute for an Element in a Document which I can use to reach that element at a later time. Let's say I inserted a paragraph to a document as follows:
var myParagraph = 'This should be highlighted when user clicks a button';
body.insertParagraph(0, myParagraph);
Then the user inserts another one at the beginning manually (i.e. by typing or pasting). Now the childIndex of my paragraph changes to 1 from 0. I want to reach that paragraph at a later time and highlight it. But because of the insertion, the childIndex is not valid anymore. There is no Id like attribute for Element interface or any type implementing that. CahceService and PropertiesService only accepts String data, so I can't store myParagraphas an Object.
Do you guys have any idea to achieve what I want?
Thanks,
Old version of the same question (Optional Read):
Imagine that user selects a word and presses the highlight button of my add-on. Then she does the same thing for several more words. Then she edits the document in a way that the start end end indexes of those highlighted words change.
At this point she presses the remove highlighting button. My add-on should disable highlighting on all previously selected words. The problem is that I don't want to scan the entire document and find any highlighted text. I just want direct access to those that previously selected.
Is there a way to do that? I tried caching selected elements. But when I get them back from the cache, I get TypeError: Cannot find function insertText in object Text. error. It seems like the type of the object or something changes in between cache.put() and cache.get().
var elements = selection.getSelectedElements();
for (var i = 0; i < elements.length; ++i) {
if (elements[i].isPartial()) {
Logger.log('partial');
var element = elements[i].getElement().asText();
var cache = CacheService.getDocumentCache();
cache.put('element', element);
var startIndex = elements[i].getStartOffset();
var endIndex = elements[i].getEndOffsetInclusive();
}
// ...
}
When I get back the element I get TypeError: Cannot find function insertText in object Text. error.
var cache = CacheService.getDocumentCache();
cache.get('text').insertText(0, ':)');
I hope I can clearly explained what I want to achieve.
One direct way is to add a bookmark, which is not dependent on subsequent document changes. It has a disadvantage: a bookmark is visible for everyone...
More interesting way is to add a named range with a unique name. Sample code is below:
function setNamedParagraph() {
var doc = DocumentApp.getActiveDocument();
// Suppose you want to remember namely the third paragraph (currently)
var par = doc.getBody().getParagraphs()[2];
Logger.log(par.getText());
var rng = doc.newRange().addElement(par);
doc.addNamedRange("My Unique Paragraph", rng);
}
function getParagraphByName() {
var doc = DocumentApp.getActiveDocument();
var rng = doc.getNamedRanges("My Unique Paragraph")[0];
if (rng) {
var par = rng.getRange().getRangeElements()[0].getElement().asParagraph();
Logger.log(par.getText());
} else {
Logger.log("Deleted!");
}
}
The first function "marks" the third paragraph as named range. The second one takes this paragraph by the range name despite subsequent document changes. Really here we need to consider the exception, when our "unique paragraph" was deleted.
Not sure if cache is the best approach. Cache is volatile, so it might happen that the cached value doesn't exist anymore. Probably PropertiesService is a better choice.
I'm using Google Forms to feed a Google Doc (via Google Sheets), and in this Google Doc I have a pre-existing bulleted list. I would like to enter code that will add another bullet to the list.
My approach has been to add a ###newLine### tag to the end of the last pre-filled bullet in the form. I then used replace.Text(###newLine###) in GAS, and then added '\n' for a new line.
The problem is that this '\n' inserts a soft line break (like a Shift+Enter), and it doesn't create a new bullet. It just creates a new line under the prior bullet. I have tested within the doc by adding/removing the bullet associated with the above paragraph, and it is clear that this new line is associated with the above paragraph. What I would like is a a hard line break (like simply pressing Enter) which will create a new bullet.
Here's what the code looks like:
body.replaceText('###newLine###', '\n' + 'Produces a soft (shift+enter) line break.');
Also tried:
body.appendParagraph().
This attached to the end of the body and didn't seem to replaceText.
var insertPar = body.insertParagraph(21, 'test insert paragraph');
body.replaceText('###newBullet###', insertPar);
This would put it in the right spot but not as part of the list.
var listItemTest = body.appendListItem('#listItemTest#');
body.replaceText('###newBullet###', listItemTest);
This appended a numbered list to the end of the body but would not replace text or add to the existing bulleted list.
08-03-19, I tried the following, per Jescanellas's assistance. It works perfectly in the original test doc I provided, but I can't port it over to other docs. I think that's because I'm somehow failing to get the right data to attach it to the list at the right level, but I'm not sure where I'm messing up.
var formDataEntered = functionName.values[11] || ''; //This var is retrieved from the sheet attached to a form. It's the submitted data.
var listItem = body.getListItems(); //We're getting the list.
for (var i = 0; i < listItem.length;i++){ //We're creating a loop here.
var item = body.getListItems()[i]; //This gets the list and applies the loop to it.
if ((item.findText('###bulletTestPlaceholder###')) && (formDataEntered != '')){ //The ###bulletTestPlaceholder### is just a placeholder in the doc where I want to insert the bullet. Your purpose with the item.findText is to identify the list level we're going for - NOT to use the text itself as a placeholder and replace the text.
var index = body.getChildIndex(item); //You're getting all the data about var item (where we got the list and applied the loop).
var level = item.getNestingLevel(); //This gets the nesting level of var item. I'm wondering if this might be the issue as it's not specific to the findText('###bulletTestPlaceholder###')?
var glyph = item.getGlyphType(); //This gets the bullet type.
body.insertListItem((index + 1), formDataEntered).setNestingLevel(level).setGlyphType(glyph); //This is the location in the list where teh bullet will be placed. It also sets the nesting level and glyph type. I've tried playing with the nesting level using integers, but that doesn't fix it.
item.replaceText('###bulletTestPlaceholder###',''); //removes '###bulletTestPlaceholder###' text after it's no longer needed.
break; //stops the loop from looping multiple times.
} else if ((item.findText('###bulletTestPlaceholder###')) && (formDataEntered == '')) {
item.replaceText('###bulletTestPlaceholder###',''); //removes '###bulletTestPlaceholder###' text and avoids new line if no formDataEntered
}
}
After some investigating, and thanks to your examples and links I think I got the solution of this. If there is any issue or I misunderstood something, please tell me and I will correct the post.
This searches for the word ###origApproach### in all the paragraphs (list items) and adds the ###formData### next to it. After this it removes the ###origApproach### paragraph. I commented some lines in case you don't want to remove it.
function myFunction() {
var body = DocumentApp.getActiveDocument().getBody();
var listItem = body.getListItems();
for (var i = 0; i < listItem.length;i++){
var item = body.getListItems()[i];
if (item.findText('###origApproach###')){
var index = body.getChildIndex(item);
var level = item.getNestingLevel();
var glyph = item.getGlyphType();
//In case the indentation is changed:
var indentFirst = item.getIndentFirstLine();
var indentStart = item.getIndentStart();
//Added both setIndents to fix the indentation issue.
body.insertListItem((index + 1), '###formData###').setNestingLevel(level).setGlyphType(glyph).setIndentFirstLine(indentFirst).setIndentStart(indent);
body.removeChild(item); //Comment this if you don't want to remove the ###origApproach### paragraph
//Uncomment this if you want to keep the paragraph and remove ###origApproach###
//item.replaceText('###origApproach###','');
break;
}
}
}
If you remove the paragraph with the word ###origApproach### change index + 1 to index
EDIT
If it's changing your glpyh style, you can force it by using the parameters BULLET, HOLLOW_BULLET, NUMBER, etc instead of the variable glyph.
You can read more about these functions here
I have a JavaScript array which contains a number of images. The images have all been taken from a hidden section in my HTML using lines such as
sources[0] = document.getElementById("building").src
The reason I am getting the images from the HTML rather than directly from the source is because I want to display around 30 of them on an HTML5 canvas, and if I were to load them directly from source into the JavaScript code, the page would take ages to load, so I've 'preloaded' them in a hidden section of my HTML, and then loaded them into the JavaScript code from there.
So, I have created my JavaScript array with the following:
var sources = [];
sources[0] = document.getElementById("building").src,
sources[1] = document.getElementById("chair").src,
....
(There are roughly 30 lines for adding images to the sources array).
The idea is that there will be four or five different 'types' of image (that is, some will be assets, others will be liabilities, etc.), and the user will be required to drag each image to its corresponding description box (also displayed on the canvas).
So what I'd like to do, is put the images into different 'groups' within the one array. I've had a look into it, and my understanding is that the best way to do this is by using associative arrays, so, for example:
var sources = {
name1 : ["name1", "place1", "data1"],
name2 : ["name2", "place2", "data2"],
name3 : ["name3", "place3", "data3"]
};
But I'm not sure how to use this in the context of what I currently have... Would I do something like the following?
var sources = {
asset1 : document.getElementById("building").src,
asset2 : document.getElementById("chair").src,
liability1: document.getElementById("electricity").src,
...
};
How would I then check whether an image belongs to the description box it's been dragged to when the user drags it to the description box they think it belongs to?
The way I intend to check whether an image has been dragged to the correct description box, is to have a Boolean that will be set to "true" when a mousedown event is detected on one of the 'draggable' images, and keep track of the x & y coordinates of the cursor as it moves around the canvas.
Then, when the mouseup event is triggered, I will check what the coordinates of the cursor are at that point. If the cursor has dropped the image to a location at which one of the description boxes are drawn (so for example, x being between 50-100 and y between 150-200 is where the assets description box is, liabilities description box at x 150-200, y 150-200), then I will check which description box is at that location (loop through the array of description box locations, and check which one is at the location of the cursor, if any).
Once I have the name of the description box at that location, I will check to see what 'type' the image that has just been dropped was, and if it matches the box at the location at which it's been dropped, it will disappear from the canvas, if not, it will be drawn back where it was originally 'picked up' by the user.
What I'm not sure about with how to do this, is how do I access the 'type' of image that the user has clicked on from the array?
Edit 2013-03-13T14:15
So I've given the suggested answer a go. However, when I now view my page in the browser, the canvas is not displayed, and I get an "uncaught exception" error in the console. This error message says:
uncaught exception: [Exception... "Component returned failure code: 0x80040111 (NS_ERROR_NOT_AVAILABLE) [nsIDOMCanvasRenderingContext2D.drawImage]" nsresult: "0x80040111 (NS_ERROR_NOT_AVAILABLE)" location: "JS frame :: file:///M:/year%205/major%20project/development/collision/kinetic.js :: :: line 4250" data: no]
I'm wondering if this is because of the functions I'm calling at the end of this function...? To explain things a bit further, I'm creating and using the sources array inside the window.onload function. It currently looks like this:
window.onload = function(){
var sources = {
assets: [],
liabilities: [],
income: [],
expenditure: []
}
console.log("the code reaches here");
sources.assets[0] = document.getElementById("building").src,
sources.assets[1] = document.getElementById("chair").src,
sources.assets[2] = document.getElementById("drink").src,
sources.assets[3] = document.getElementById("food").src,
sources.assets[4] = document.getElementById("fridge").src,
sources.assets[5] = document.getElementById("land").src,
sources.assets[6] = document.getElementById("money").src,
sources.assets[7] = document.getElementById("oven").src,
sources.assets[8] = document.getElementById("table").src,
sources.assets[9] = document.getElementById("van").src,
sources.liabilities[10] = document.getElementById("burger").src,
sources.liabilities[11] = document.getElementById("chips").src,
/* I have loads of lines like this, adding in roughly 30 images */
/*Maybe create an array of attributes within each position
of this array, so that I have access to all of each
element's attributes. */
/*Create an associative array for the images and their types*/
var imageTypes = new Array();
/*Use a loop to populate the associative array, declare variables for the total number
of items in each group, declare a variable for the item being for example: */
var numAssets = 10;
var numLiabilities = 5;
var numEx = 11;
var numInc = 8;
// Error checking- check total of these numbers adds up to the number elements in sources array.
var j = 0; // This is to indicate the location of the image in sources array
loadImages(sources, drawImage);
drawGameElements();
drawDescriptionBoxes();
//drawBox();
stage.add(imagesLayer);
};
Is the canvas now not showing because of one of the other functions I'm calling at the end of the window.onload function? But these function calls were performing correctly before I changed my sources array, so maybe it something wrong with what I've changed in the sources array?
I'd suggest restructuring your sources object to:
var sources = {
assets: [],
liabilities: []
};
sources.assets[0] = document.getElementById("building").src;
sources.assets[1] = document.getElementById("chair").src;
...
sources.liabilities[0] = document.getElementById("electricity").src;
...
The sources object is optional, you can just have the assets and liabilities arrays.
You can then use this answer:
How to determine if object is in array
to figure out the 'type' of the selected image.
Alternatively, you could add a class to the images depending on their 'type' and then check that when the user selects.
I am creating a templating system which can be interpreted at client side with Javascript to construct a fill in the blanks form e.g. for a letter to a customer etc.
I have the template constructed and the logic set out in pseudo code, however my unfamiliarity with jQuery I could use some direction to get me started.
The basic idea is there is a markup in my text node that denotes a field e.g. ${prologue} this is then added to an array called "fields" which will then be used to search for corresponding node names in the xml.
XML
<?xml version="1.0" encoding="UTF-8"?>
<message>
<text>${Prologue} - Dear ${Title} ${Surname}. This is a message from FUBAR. An engineer called but was unable to gain access, a new appointment has been made for ${ProductName} with order number ${VOLNumber}, on ${AppointmentDate} between ${AppointmentSlot}.
Please ensure you are available at your premises for the engineer. If this is not convenient, go to fubar.com or call 124125121515 before 12:00 noon the day before your appointment. Please refer to your order confirmation for details on what will happen on the day. ${Epilogue} - Free text field for advisor input<
</text>
<inputTypes>
<textBox type="text" fixed="n" size="100" alt="Enter a value">
<Prologue size="200" value="BT ENG Appt Reschedule 254159" alt="Prologue field"></Prologue>
<Surname value="Hoskins"></Surname>
<ProductName value=""></ProductName>
<VOLNumber size="8" value="" ></VOLNumber>
<Epilogue value=""></Epilogue>
</textBox>
<date type="datePicker" fixed="n" size="8" alt="Select a suitable appointment date">
<AppointmentDate></AppointmentDate>
</date>
<select type="select" >
<Title alt="Select the customers title">
<values>
<Mr selected="true">Mr</Mr>
<Miss>Miss</Miss>
<Mrs>Mrs</Mrs>
<Dr>Dr</Dr>
<Sir>Sir</Sir>
</values>
</Title>
<AppointmentSlot alt="Select the appointment slot">
<values>
<Morning>9:30am - 12:00pm</Morning>
<Afternoon>1:00pm - 5:00pm</Afternoon>
<Evening>6:00pm - 9:00pm</Evening>
</values>
</AppointmentSlot>
</select>
</inputTypes>
</message>
Pseudocode
Get list of tags from text node and build array called "fields"
For each item in "fields" array:
Find node in xml that equals array item's name
Get attributes of that node
Jump to parent node
Get attributes of parent node
If attributes of parent node != child node then ignore
Else add the parent attributes to the result
Build html for field using all the data gathered from above
Addendums
Is this logic ok, is it possible to start at the parent of the node and navigate downwards instead?
Also with regards to inheritence could we get the parent attributes and if the child attributes are different then add them to the result? What about if the number of attributes in the parent does not equal the number in the child?
Please do not provide fully coded solutions, just a little teasers to get me started.
Here is what I have so far which is extracting the tags from text node
//get value of node "text" in xml
var start = $(xml).find("text").text().indexOf('$');
var end = $(xml).find("text").text().indexOf('}');
var tag = "";
var inputType;
// find all tags and add them to a tag array
while (start >= 0)
{
//console.log("Reach In Loop " + start)
tag = theLetter.slice(start + 2, end);
tagArray.push(tag);
tagReplaceArray.push(theLetter.slice(start, end + 1));
start = theLetter.indexOf('$', start + 1);
end = theLetter.indexOf('}', end + 1);
}
Any other recommendations or links to similar problems would be welcome.
Thankyou!
I am using a similar technique to do html templating.
Instead of working with elements, I find it easier to work with a string and then convert it to html. In your case with jQuery, you could do something similar:
Have your xml as a string:
var xmlString='<?xml version="1.0" encoding="UTF-8"?><message><text>${Prologue} - Dear ${Title} ${Surname}... ';
Iterate through the string to do the replacements with a regex ($1 is the captured placeholder, for example Surname):
xmlString.replace(/$\{([^}]+)}/g,function($0,$1)...}
Convert to nodes if needed:
var xml=$(xmlString);
The benefits of the regex:
faster (just a string, you're not walking the DOM)
global replace (for example if Surname appears several times), just loop through your object properties once
simple regex /${([^}]+)}/ to target the placeholder
Get list of tags from text node and build array called "fields"
To create the array I would rather user regular expression, this is one of the best use for it (in my opinion) because we are indeed searching for a pattern :
var reg = /\$\{(\w+)\}/gm;
var i = 0;
var fields = new Array();
while ( (m = reg.exec(txt)) !== null)
{
fields[i++] = m[1];
}
For each item in "fields" array
jQuery offers some utility functions :
To iterate through your fields you could do this : $.each(fields, function(index, value){});
Navigating through the nodes and retrieving the values
Just use the jQuery function like you are already doing.
Building the HTML
I would create templates objects for each types you would take in charge (in this example : Text, Select)
Then using said templates you could replace the tokens with the HTML of your templates.
Displaying the HTML
Last step would be to parse the result string and append it at the right place:
var ResultForm = $.parseHTML(txt);
$("#DisplayDiv").append(ResultForm);
Conclusion
Like you asked, I did not prepare anything that works right out of the box, I hope it will help you prepare your own answer. (And then I hope you will share it with the community)
This is just a framework to get you going, like you asked.
first concept is using a regex to just find all matches of ${ }. it returns an array like ["${one}","${t w 0 }","${ three}"].
second concept is a htmlGenerator json object mapping "inputTypes-->childname" to a function responsible for the html print out.
third is not to forget about natural javascript. .localname will give you the xml element's name, and node.attributes should give you a namedNodeMap back (remember not to perform natural javascript against the jquery object, make sure you're referencing the node element jQuery found for you).
the actual flow is simple.
find all the '${}'tokens and store the result in an array.
find all the tokens in the xml document and using their parents info, store the html in an map of {"${one}":"<input type='text' .../>","${two}":"<select><option value='hello'>world!</option></select>" ...}
iterate through the map and replace every token in the source text with the html you want.
javascript
var $xmlDoc = $(xml); //store the xml document
var tokenSource =$xmlDoc.find("message text").text();
var tokenizer=/${[^}]+/g; //used to find replacement locations
var htmlGenerators = {
"textBox":function(name,$elementParent){
//default javascript .attributes returns a namedNodeMap, I think jquery can handle it, otherwise parse the .attributes return into an array or json obj first.
var parentAttributes = ($elementParent[0] && $elementParent.attributes)?$elementParent.attributes:null;
//this may be not enough null check work, but you get the idea
var specificAttributes =$elementParent.find(name)[0].attributes;
var combinedAttributes = {};
if(parentAttributes && specificAttributes){
//extend or overwrite the contents of the first obj with contents from 2nd, then 3rd, ... then nth [$.extend()](http://api.jquery.com/jQuery.extend/)
$.extend(combinedAttributes,parentAttributes,specificAttributes);
}
return $("<input>",combinedAttributes);
},
"date":function(name,$elementParent){
//whatever you want to do for a 'date' text input
},
"select":function(name,$elementParent){
//put in a default select box implementation, obviously you'll need to copy options attributes too in addition to their value / visible value.
}
};
var html={};
var tokens = tokenSource.match(tokenizer); //pull out each ${elementKey}
for(index in tokens){
var elementKey = tokens[index].replace("${","").replace("}"),"");//chomp${,}
var $elementParent = $xmlDoc.find(elementKey).parent();//we need parent attributes. javascript .localname should have the element name of your xml node, in this case "textBox","date" or "select". might need a [0].localname....
var elementFunction = ($elementParent.localname)?htmlGenerators[elementParent.localname]:null; //lookup the html generator function
if(elementFunction != null){ //make sure we found one
html[tokens[index]] = elementFunction(elementKey,elementParent);//store the result
}
}
for(index in html){
//for every html result, replace it's token
tokenSource = tokenSource.replace(index,html[index]);
}
Is there any way of using a Dropdown Menu as opposed to the checkbox's that are used in the examples of the Store Locator Library for Maps API. The checkbox is a 'storeLocator.Feature' item.
Essentially I want the user to be able to select an item from the dropdown list and this instantly change the markers on the map.
I am very new to Javascript coding but experienced in CSS, HTML and other computer languages. I have followed the examples in the link fairly closely so you can assume my own code looks the same. –
Here is the section of code i think i have to edit:
DataSource.prototype.parse_ = function(csv) {
var stores = [];
var rows = csv.split('\n');
var headings = this.parseRow_(rows[0]);
for (var i = 1, row; row = rows[i]; i++) {
row = this.toObject_(headings, this.parseRow_(row));
var features = new storeLocator.FeatureSet;
features.add(this.FEATURES_.getById('Cafe-' + row.Cafe));
features.add(this.FEATURES_.getById('Wheelchair-' + row.Wheelchair));
features.add(this.FEATURES_.getById('Audio-' + row.Audio));
var position = new google.maps.LatLng(row.Ycoord, row.Xcoord);
var shop = this.join_([row.Shp_num_an, row.Shp_centre], ', ');
var locality = this.join_([row.Locality, row.Postcode], ', ');
var store = new storeLocator.Store(row.uuid, position, features, {
title: row.Fcilty_nam,
address: this.join_([shop, row.Street_add, locality], '<br>'),
hours: row.Hrs_of_bus
});
stores.push(store);
}
return stores;
};
Thanks.
you need to follow these steps:
set the featureFilter-option of the panel to false
(this will prevent the library from creating the checkboxes)
create a variable where you store all features for later use:
var features=view.getFeatures().asList();
this returns an array with all features
create the select-element
populate the select-element with the needed option-elements
iterate over the features-array created above and append an option for every item to the select .
The text to display inside the option you get by calling the getDisplayName()-method of the item.
add a change-handler to the select with the following callback:
function(){
view.set('featureFilter',
new storeLocator.FeatureSet(features[this.selectedIndex]));
view.refreshView();}
(where view is the storeLocator.View and features the array created in step#2)
5. put the select to the desired place inside the document
Hope i'm allowed to comment here as i found this question very useful for my implementation of store locator.
Dr Molle's solution in the JS fiddle is excellent however i've just noticed that the 'directions' functionality of the map no longer works. Could this easily be rectified? thanks
edit: easier than i thought. In the fiddle "featureFilter:" is set to 'false'. A div with the class="feature-filter" needs to be present in the code for the directions to appear, setting the value to 'true' shows the div (and checkboxes) so that directions work. Checkboxes were hidden in the stylesheet..
.storelocator-panel .feature-filter {
/*overflow: hidden;*/ display:none
}
This may be useful to someone