Removing Child Elements from Symbol - javascript

I'm trying to implement a History with Adobe Edge Animate and somewhere in a trigger I have to update it. What I'm trying to do is deleting all previous symbols and rewrite them with the updated history content.
This is how I'm creating child symbols:
// Creating child symbols
$.each( h.reverse(), function(index, item){
var itemRenderer = sym.createChildSymbol("ItemRenderer", "HistoryContainer");
itemRenderer.$("ItemText").text(item);
});
Then I try to get all of the child symbols in order to delete them:
// Delete all child symbols
var cs = sym.$("HistoryContainer").getChildSymbols();
And I get:
Javascript error in event handler! Event Type = timeline
How am I able to get the list of childSymbols and update the HistoryContainer?

Replace this:
sym.$("HistoryContainer").getChildSymbols();
with:
sym.getSymbol("HistoryContainer").getChildSymbols();
With your code you are getting the div element (as a jQuery element). In order to have the getChildSymbol method you need to get the AdobeEdge symbol.

Related

Google Scripts - keep track of element [duplicate]

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.

Typescript: Access variable outside of a loop

I've created an array and want to access the created elements outside of the loop. I know that they are not in the scope and writing this. infront of it wont make them accessible either.
colIdx = colIdx + this.columns.findIndex(c => c.editable);
this.focusInput(rowIdx, colIdx);
this.autocompletes.toArray().forEach(el => {
console.log(el);
})
I have a table with lots of input fields with an autocomplete function and a suggestion panel. I also have a custom method which lets the user tab with the enter key. However at first, tabbing with enter didnt close the suggestion panel of the autocomplete hence after a while all the suggestion panels where open.
Thats why I've created the method above. You could actually ignore the first two lines because they are needed for the tabbing with enter.
this.autocompletes is a Querylist with all my input elements. I've turned them into an array and called each element el.
This way I'm able to call a closePanel() method on el in order to close the suggestion panels of the autocomplete method. However doing it this way shuts down the suggestion panels of ALL el elements. Thats why I need the index of el the user has set focus on and close this one.
In order to do so I have to access el outside the for-loop it has been created in.
You can initialize an empty array outside the loop like var arr: type = emptyArr[]; and then push the data (el) in it from inside the loop.
To push do something like: arr.push(el) from inside the loop and then access it outside the loop.
The easiest way would be to just assign it them to an array outside the scope of the loop:
elements: any[] = [];
colIdx = colIdx + this.columns.findIndex(c => c.editable);
this.focusInput(rowIdx, colIdx);
this.autocompletes.toArray().forEach(el => {
console.log(el);
this.elements.push(el);
});
// You can now loop over `this.elements` and do what you need with them.
this.autocompletes is a Querylist with all my input elements
This means autocompletes won't change and you can save it in your Init call into a local variable.
ex: if your form have 4 input it will still contain 4 unless you are removing it from the dom. Save it in a local variable during your create/init. depending how you have written your code.
Here is what you can do.
//do not do .toArray again and again its just not usefull as your data is constant
elements: Array<any> = this.autocompletes.toArray();
...
elements.forEach(el => {
//work on elements here
//to check focus on el you could do something like this el.is(":focus")
});

Set attribute for custom DOM nodes in text preview component

I want to add / setAttribute class for custom DOM nodes while writing some text in custom bbcode-like text editor. When the innerHTML of <item></item> is not empty I'm filtering through items array in order to find the value that matches. There can be unlimited amount of item nodes. (i.e 2, 5, 10)
So whenever i click on icon named item it shows in textarea as [item][/item] and in preview component as <item></item>. Once the item is written, lets say [item]id123[/item] I have in DOM <item>itemName123</item>.
Now, what I'm doing is manipulating the DOM outside React with:
const setAttributes = (el, item) =>{
el.dataset.img = item.img;
el.setAttribute('class', _.toLower(item.color))
};
const updateItems = () =>{
if(document.querySelectorAll('item')) {
document.querySelectorAll('item').forEach(el => items.find(item => {
if(_.toLower(el.innerHTML) === _.toLower(item.id))
setAttributes(el, item)
}));
}
}
The problem is, whenever I change the text, component re-renders so that removes attributes that have been set.
I tried to manipulate the text/string before it goes to dangerouslySetInnerHTML markup by splitting it, going through includes with map, filter all that godsent functions with some regex sauce from ES6 but it just smells bad.
It feels so hacky that i believe that there has to be something that I'm missing.
Edit: Forgot to add that I've also tried to put setAttributes() && updateItems() outside of class.
Edit#2: The way i'm changing from [item][/item] is via regexp text.replace(/\[item]/g, <item>).replace(/\[\/item]/g, </item>), so probably i could do something with regexp instead of setAtrribute on each re-render?And if so, i've been trying that via
new RegExp(/\[item]/+ _.toLower(item.name)+ /\[\/item]/, 'g');
and later on text.replace(<item class="${item.quality}">${_.toLower(item.name)}</item>)
but no success so far.
Thanks in advance for any tips and ideas!
Solved. Used RegExp + pattern.exec(text) and looping through text with while and replacing any matched occurances. Then used for(let..of) loop to compare the matched values and overwrite the value.

Protractor - get child element of an element?

I am trying to access child element of an ng-repeat element but I am having troubles doing that.
I have searched around about the problem and the solutions that I have found did not work for me. One of those solutions was to do something like this:
var parent = element(by.repeater(''));
var child = parent.element(by.....);
When I try the child line I cant see the element function on the parent element..
http://prikachi.com/images/11/8338011u.png
If you see the screenshot above you will see the structure of the code of the page that I am trying to test.
I need to access the alt attribute of the image of the avatar and get its value (thats the Username of the User).
One thing that came to my mind is to use .getInnerHTML() on the ng-repeat row which will return a string with all that code. From there I can find the alt attribute and its value with string manipulation but this seems too brute and I am sure that there has to be a better way.
Simply I want to be able to get row 4 from the repeater and get the Username of the user at row 4, that's all I wanna do actually.
Try this,
var parent = element(by.repeater('f in feed'));
var child = parent.all(by.xpath('//img[#alt="Pundeep"]')).first()
(or)
var parent = element(by.repeater('f in feed'));
var child = parent.all(by.xpath('//img[#alt="Pundeep"]')).get(0)
You can get it directly using element.all() and get() locator in protractor. Here's how -
var child = element.all(by.repeater('parent_locator')).get(3); //gets 4th element in repeater. Its a 0 based index.
child.getAttribute('alt').then(function(user){
var username = user; //username contains the alt text
});
Hope this helps.
In Protractor element documentation it gives an example like this to find child elements, which is same as chaining element find:
// Chain 2 element calls.
let child = element(by.css('.parent')).
$('.child');
expect(child.getText()).toBe('Child text\n555-123-4567');
// Chain 3 element calls.
let triple = element(by.css('.parent')).
$('.child').
element(by.binding('person.phone'));
expect(triple.getText()).toBe('555-123-4567');
// Or using the shortcut $() notation instead of element(by.css()):
// Chain 2 element calls.
let child = $('.parent').$('.child');
expect(child.getText()).toBe('Child text\n555-123-4567');
// Chain 3 element calls.
let triple = $('.parent').$('.child').
element(by.binding('person.phone'));
expect(triple.getText()).toBe('555-123-4567');
https://www.protractortest.org/#/api?view=ElementFinder.prototype.$
this example could help :
return element(by.css('select.custom-select:nth-child(1) option[value="12"]'));
you can use nth-child() selector to access to a child element.
In my example i used a plugin with 2 select with same classes and i wanted to click on a defined option in the select 1, and a second in the select 2.

How to create a string and use a function that's named after it?

Sorry for bad wording in the question but it's hard to explain for me. I'm using several bxsliders on a page and some are placed in hidden divs. Unfortunately images are not shown in the slider after making the parent div visible unless the slider is reloaded (See here: bxSlider within show/hide divs). So let's say I initiate the sliders at the beginning with:
var slider_0=$("#slider_0 .bxslider").bxSlider({
//bxslider options here
});
var slider_4=$("#slider_4 .bxslider").bxSlider({
//bxslider options here
});
var slider_7=$("#slider_7 .bxslider").bxSlider({
//bxslider options here
});
The sliders are not consecutively numbered but there is a navigation and if I click the 7th element it leads to slider_7. So I could get the index of the clicked item with:
$(this).index();
When I call slider_7.reloadSlider(); it would work but I don't know which slider the user clicks and which number it has. So would it be possible to call that with a created string like this:
slider_name='slider_'+$(this).index();
slider_name.reloadSlider();
works not of course. Is there a way to do it?
I would create a dictionary with strings as keys and functions as values. Then, you could have O(1) lookup of the functions you're targeting.
In general, you can do it like so:
// set up your dictionary
var dictionary = {};
// add your functions
dictionary['methodName'] = function() {};
// call the functions
dictionary['methodName']();
So, for your example, you could do:
dictionary['slider_7'] = slider_7.reloadSlider;
dictionary['slider_'+$(this).index()]();
You could trigger it with
window["slider_" + $(this).index()].reloadSlider()
Although, I'm not sure whether your approach is the best. I think I'd go with arrays or with object (as a key-value pairs)
Try this:
slider_name='slider_'+$(this).index();
$("#" + slider_name + " .bx_slider").reloadSlider();
Found a working solution:
eval("slider_" + $(this).index()).reloadSlider();
Its not entirely clear here what you want/are trying to do. What it seems like you want to do is get a programmatic handle on a specific slider when a user clicks a specific part of your page. You do not accomplish this by eval()ing a string...that's what event handlers are for. So create a click event handler and in that event handler
$('#idOfWhatTheUserClicksOn').click(function(event) {
var slider = document.getElementById('#idOfRelatedSlider');
$(slider).bxSlider();
//if you need the current value of the slider you can get that too
var value = slider.value;
});
You could achieve the same with fewer LOC by using a class instead of id's with different handlers, but the concept is the same.
var slider_cache = [
$("#slider_0 .bxslider").bxSlider(),
$("#slider_1 .bxslider").bxSlider(),
$("#slider_2 .bxslider").bxSlider()
];
...
slider_cache[$(this).index()].reloadSlider();

Categories

Resources