CKEDITOR instances not recognized in for loop - javascript

I am adding new textarea elements via AJAX, so I wrote the function assignRichText() below which adds CKEDITOR to any textarea.addckeditor, then removes the addckeditor class so it isn't added a second time later. This seems to work.
However, I am also calling the function updateRichText() below when doing AJAX saves, because otherwise my textarea elements are not updated (since I'm not submitting the form). In my console I see that the function is starting and ending, however the console.log('i='+i); never puts anything in my console. So my instances are not being picked up. Is there something wrong with my "for" statement?
function assignRichText(){
$('textarea.addckeditor').each(function(){
ClassicEditor
.create(document.querySelector('textarea.addckeditor'), {
toolbar: ['bold', 'italic']
})
.then(editor => {
console.log(editor);
})
.catch(error => {
console.error(error);
});
$(this).removeClass('addckeditor');
});
}
function updateRichText(){
console.log('start updateRichText');
for (var i in ClassicEditor.instances){
console.log('i='+i);
ClassicEditor.instances[i].updateElement();
}
console.log('end updateRichText');
}

Related

doc.createTreeWalker is not a function

When I click the button with text 'Create' the text is replaced to 'Confirm?'
This is the HTML:
and the pageObject:
create() {
return cy.get('im-page.hydrated', { includeShadowDom: true })
.find('im-button', { includeShadowDom: true })
.eq(1)
.find('button', { includeShadowDom: true })
.click({ force: true })
}
confirmBtn() {
return cy.get('im-page.hydrated').find('im-button')
.eq(1)
.find('button.success.outline')
.contains('Confirm?')
}
Then when Cypress click on Confirm I got this error:
There's an issue logged doc.createTreeWalker is not a function #20813 but not yet resolved.
From the source code the doc part refers to the previous subject, i.e the element before the .find() which above is cy.get('im-page.hydrated').find('im-button').eq(1).
My guess is because the button changes on the Create click, something in that previous subject becomes invalid when you try to access the Confirm button.
A couple of ideas to try (just guesses at this stage)
// using jquery to avoid the treeWalker (used by the .find() command)
cy.get('im-page.hydrated im-button:eq(1) button.success.outline:contains(Confirm)')
// using an alias and "withinSubject" option to explicitly define `doc`
cy.get('im-page.hydrated im-button:eq(1)`).as('parent')
cy.get('#parent').then($parent => {
cy.get('button.success.outline:contains(Confirm)', { withinSubject: $parent })
You should turn on shadowDOM globally to avoid missing any parts that need it.
// cypress.json
{
...
"includeShadowDom": true
}
#3 - just do a simple search for "Confirm", since likely only one thing at a time needs confirming.
cy.contains('button.success.outline', 'Confirm')

intern.js refactoring to before block behavior not consistent

I'm using the intern.js library with Chai and BDD to test my javascript application. I have the following code:
// Login as admin
bdd.before(function() {
indexPage = new IndexPage(this.remote, adminUsername, adminPass);
});
bdd.it('should turn a user to an input box', function () {
return indexPage.login(baseUrl)
.clearLocalStorage()
.get(baseUrl + '#/details')
.findAllByCssSelector('.user-filter')
.findByName('user')
.clearValue()
.click().pressKeys(['Functional Test', '\uE015', '\uE006'])
.end()
.findByXpath('//td[#class="grid-column-user"]/span')
.click()
.end()
.findByXpath('//td[#class="grid-column-user"]/input')
.then(function (elem) {
assert.lengthOf(elem, 1, "Yay");
})
.end();
});
bdd.it('should get the error state class when incorrect input is added', function () {
return indexPage.login(baseUrl)
.clearLocalStorage()
.get(baseUrl + '#/details')
.findAllByCssSelector('.user-filter')
.findByName('user')
.clearValue()
.click().pressKeys(['Functional Tes', '\uE015', '\uE006'])
.end()
.findByXpath('//td[#class="grid-column-user"]/span')
.click()
.pressKeys(['adsf', '\uE006'])
.end()
.findByXpath('//td[#class="grid-column-user"]/input[#class="user-error"]')
.then(function (elem) {
assert.lengthOf(elem, 1, "User should be input");
})
.end();
});
So I want to extrapolate out a lot of the logic that is duplicated between the tests. It seems like the following code could be in the before block:
bdd.before(function() {
indexPage = new IndexPage(this.remote, adminUsername, adminPass);
testUser = indexPage.login(baseUrl)
.clearLocalStorage()
.get(baseUrl + '#/details')
.findAllByCssSelector('.user-filter')
.findByName('user')
.clearValue()
.click().pressKeys(['Functional Test', '\uE015', '\uE006'])
});
bdd.it('should get the error state class when incorrect input is added', function () {
return testUser.end()
.findByXpath('//td[#class="grid-column-user"]/span')
.click()
.pressKeys(['adsf', '\uE006'])
.end()
.findByXpath('//td[#class="grid-column-user"]/input[#class="user-error"]')
.then(function (elem) {
assert.lengthOf(elem, 1, "User should be input");
})
.end();
});
When I put this code into the before block and store it as a variable, the behavior of the code doesn't run as it did when it was all in one long chained call and not in the before block. I'm not sure what I'm doing wrong here as I've tried multiple different iterations on what I've extrapolated out.
Thanks!
In your original code, you're resetting the page state for each test by logging in to a new session and clearing local storage. In your new code, you're only doing that once at the beginning of the suite, so all of the tests within the suite are going to run in the same session on the test page.
To replicate the behavior of your original tests, use a beforeEach rather than a before.

Why isn't my data been added to the stash in mojolicious?

I'm having an issue with Mojolicious and the stash and I think I'm probably just not understanding the way it works?
I have a page with 2 combo boxes and when the first entry changes I wish to update the options in the second.
So I add an event handler like below, which then calls my controller sub routine 'devicecommandset' and then puts the result of a DBIx query into an array of hashes which I add to my stash.
I am then just render some benign text. My subroutine gets called and there is the expected contents in '#commandsets'. However I cannot see it in the stash on the browsers console ( I'm running in debug mode ).
Do I need to actually modify the DOM for the stash to be populated? Basically I'm just trying to get data back from my request to fill the combobox options.
In my template
$(document).ready(function() {
$('select:not([name*="command"])').live('change', function (e) {
$.get('devicecommandset', { device: $(this).attr("value") },
function (data) {
alert("Made it this far");
});
});
});
In my Controller
sub devicecommandset {
my $self = shift;
my $device = $self->param('device') || '';
my #commandsets = $self->db->resultset('CommandSet')->search_commandsets_by_devicename($device);
$self->stash(commandsets => \#commandsets );
print Dumper(#commandsets);
$self->render(text => 'success' );
}
You're printing a dumper to the log basically, not the browser. Your stash is not used in the render because you're not referencing it. Use inline render type and the "dumper" helper.
Try:
$self->stash(commandsets => \#commandsets );
$self->render( inline => '<%= dumper $commandsets %>' );

Dynamically adding script to a div does not execute the script

PROBLEM:
Why does this not show the alert? And how can I make it so?
<script>
function onSuccess() {
var response= "<script> alert(1);</\script>";
document.getElementById("xxx").innerHTML = response;
}
</script>
<div id="xxx">existing text</div>
<button id="click" onclick="onSuccess();">click</button>
http://jsfiddle.net/7hWRR/1/
This is just a simplified version of my problem. In our application (in one very old module in particular) we use an ancient home-grown AJAX class which just innerHTMLs all AJAX responses.Traditionally we have only sent back HTML as AJAX response but I would like to execute JS in the success handler.I do not have access to the JS file so cannot modify the way the response is handled. I can only work with the fact that the success handler calls div.innerHTML='<my response>'
So stupid as it may be, I'm hoping for some help using these constraints!
SIMILAR LINKS:
Dynamically adding script element to a div does not execute the script
Dynamically added script will not execute
Caveat: Here I'm assuming the <div> on which the results are inserted is known.
A possible solution is to use a MutationObserver (and the DOMNodeInserted event, to support IE 9 and 10) to watch said <div> for changes on its contents, and execute the code on any inserted <script> tags.
Example built upon your jsFiddle:
watchNodeForScripts(document.getElementById("xxx"));
function watchNodeForScripts(scriptRecipient) {
if ('MutationObserver' in window) {
// Prefer MutationObserver: https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
watchUsingMutationObserver();
} else {
// Fallback to Mutation Events: https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Events/Mutation_events
watchUsingDeprecatedMutationEvents();
}
function watchUsingMutationObserver() {
var observer = new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
var i, addedNodes = mutation.addedNodes;
for (i = 0; i < addedNodes.length; i++) {
handleAddedNode(addedNodes[i]);
}
});
});
observer.observe(scriptRecipient, {
childList: true
});
}
function watchUsingDeprecatedMutationEvents() {
scriptRecipient.addEventListener("DOMNodeInserted", function (event) {
handleAddedNode(event.target);
});
}
function handleAddedNode(node) {
// Don't try to execute non-script elements
if (!(node instanceof HTMLScriptElement)) return;
// Don't try to execute linked scripts
if (node.src !== "") return;
// Use 'new Function' instead of eval to avoid
// the creation of a (undesired) closure
fn = new Function(node.textContent);
fn.call(window);
}
}
Updated fiddle: http://jsfiddle.net/7hWRR/13/
Edit: Changed innerText to the more cross-compatible textContent.
Edit2: Don't execute code that isn't inside a <script> element.
Edit3: Don't execute scripts with the src attribute, and add mutation events fallback

CKEditor: HowTo destroy instance on blur?

I have a single html page with multiple div elements on it. Each time a user clicks on on a div, it is replaced with an CKEditor on the fly:
$('.editable').click(function() {
editor = CKEDITOR.replace(this);
});
Now, if the CKEditor instance loses its focus (blur event), I need to post the content to a separate script via ajax (if something changed) and destroy this instance:
$('.editable').click(function() {
editor = CKEDITOR.replace(this);
editor.on('blur', function(e)
{
if (e.editor.checkDirty())
// get data with e.editor.getData() and do some ajax magic
e.editor.destroy();
});
});
But this example won't work because, I don't know why, destory() will be called before checkDirty(). How can I get this working?
How about if you put the destroy() inside the if() statement? You could have an else clause that invokes destroy if nothing has changed. If something has changed, you can invoke destroy() within the if clause once the data has been transfered.
$('.editable').click(function() {
editor = CKEDITOR.replace(this);
editor.on('blur', function(e)
{
if (e.editor.checkDirty()) {
// get data with e.editor.getData() and do some ajax magic
if ( dataTransferComplete ) {
e.editor.destroy();
}
} else {
e.editor.destroy();
}
});
});
Or you could check a variable before invoking destroy(). Set that variable to true after the data transfer has been completed and in the else clause, that way destroy() won't be invoked until you've checked for changes and transfered any updated data.
$('.editable').click(function() {
editor = CKEDITOR.replace(this);
editor.on('blur', function(e)
{
var okToDestroy = false;
if (e.editor.checkDirty()) {
// get data with e.editor.getData() and do some ajax magic
okToDestroy = true;
} else {
okToDestroy = true;
}
if (okToDestroy )
e.editor.destroy();
});
});
This is an outline, I haven't tested the code, but if shows the concept.
Be Well,
Joe

Categories

Resources