YUI Autocomplete: itemSelectEvent getting lost with IE6 and IE7? - autocomplete

I'm using YUI Autocomplete (latest version loaded using loader as of today (May 14th, 2010), which looks to be 2.8.1, with the following options:
ac = new YAHOO.widget.AutoComplete("mynode", "autocomp_node",
ac_ds, {typeAhead: true, forceSelection: true});
ac.itemSelectEvent.subscribe( function(type, args) {
alert("hey:" + args[2][1]);
$('#parent_id').val(args[2][1]);
});
The itemSelectEvent catches selections in AutoComplete and fills in some data on the parent.
This works on FF, Chrome, Safari, and IE8. On IE6 and IE7, however, the event never seems to trigger. To replicate: In the autocomplete field, allow it to autofill for you, then hit enter. This should select the autofill and move on to the next field (that's what it does in other browsers). With IE6 and IE7 it seems to instead trigger the form submission - the itemSelectEvent never fires (or perhaps fires after the form submission?).
Has anyone seen this? Any work-arounds?

Below is an excerpt from YUI's "AutoComplete Control: Searching Field A, Submitting Field B with itemSelectEvent" example. In reality, it is the "preventDefault" aspect of the example that is applicable. Perhaps you could use something similar to block the form submission. However, I am not sure if this would prevent other events from being fired (i.e. itemSelectEvent).
// Rather than submit the form,
// alert the stored ID instead
var onFormSubmit = function(e, myForm) {
YAHOO.util.Event.preventDefault(e);
alert("Company ID: " + myHiddenField.value);
};
YAHOO.util.Event.addListener(YAHOO.util.Dom.get("myForm"), "submit", onFormSubmit);

Related

Issues with Drupal's autocomplete test with Behat

I'm trying to test a form generated by Drupal where there are some autocompletion to help people do the right choice.
In this form, there are 3 fields that let the user select 3 articles ('Actualités'), and once you start typing things, the auto-completes generates a list of Articles that have a similar title.
Here is an example:
The problem is that I realized while doing some tests that using $this->getSession()->getPage()->fillField() makes Behat loose focus on the field, like if it presses Tab once done filling the field.
So I tried a lot of tricks to gain focus of this field, like the following:
$field = $this->getSession()->getPage()->findField('field_mb_actualites[0][target_id]');
if($field) {
// Solution #1
// the dropdown menu is inside a `ul#ui-id-1` element
$this->getSession()->executeScript("jQuery('#ui-id-1').show();");
// Solution #2
$field->focus();
// Solution #3
// Consists of pressing "shift + tab" for a "reverse tab" and trying to get back to the field
$this->getSession()->getDriver()->keyDown($xpath, 16);
$this->getSession()->getDriver()->keyDown($xpath, 9);
$this->getSession()->getDriver()->keyUp($xpath, 9);
$this->getSession()->getDriver()->keyUp($xpath, 16);
// And I take a screenshot at the end to see if anything worked
file_put_contents('screenshot-focus.png', $this->getSession()->getDriver()->getScreenshot());
}
But I always get the same result, which is the following:
All I need to do once I have the focus is to press the "right" directional arrow to make the dropdown visible again, then the "down" directional arrow to select a suggestion, then "Enter" to confirm the choice.
However, I'm starting to run out of ideas, even though I found a couple questions on Behat's github about this problem, but I couldn't manage to make the answers work. How can I trigger the auto-complete?
Thank you in advance
You can use a JavaScript function to trigger the event like this:
public function triggerEventOn($css_selector){
$function = <<<JS
(function(){
var element = document.querySelector("$css_selector");
var event = document.createEvent('Event');
event.initEvent('change', true, true);
element.dispatchEvent(event);
})()
JS;
try{
$this->getSession()->getDriver()->executeScript($function);
}catch (Exception $e){
}
}
If you want you can also adapt this to fill the field and trigger the event after.
For checking the event that you need to trigger you need to inspect that element and check that ev in the inspector.

In-line item editing in Lift / handling 2 different form submit needs on one page

OK, so first off, let's start with me acknowledging that the bind( ... ) way of binding Lift forms is so last week! :) I do know that, and I just haven't gone back to update this code yet. Also, I trust now that there's some really slick Lifty way to do this. That's what I seek. I'm stumped as to even how to hack something together. That said...
I have a list of Items that I initially display non-editable, and the title of each Item is an ajax-enabled link that calls to the server and replaces that line-item with an editable form of the Item (I use SetHtml to swap the form in at the < li> that listed that Item).
"parent" Items List view looks something like this
< form data-lift="form.ajax">
< div data-lift="creategamewizard?multipart=true" id="wizardform">
< ul>
< li>Item 1< /li>
< li>Item 2< /li>
< /ul>
some more form elements
< button>Submit< /button>
< input type="hidden" id='298356928734' />
< /div>
< form>
This ajax submit (via the hidden field) calls processSubmit().
The SetHtml that swaps in the editableItem form looks something like this.
NOTE: At the end of the following listing, the "save" binding has no server-side code tied to it because the "parent" submit button is already on the page, and when I put another hidden field in this binding or tried to tie any code directly to the Edit Item Save button, that code and the "parent" submit got triggered. So the approach below was to try to use the "parent" submit for both the parent submit as well as the Edit Item submit.
<a href="javascript://" onclick={ajaxOnClickHandler(editItemClickHandler(item.id.get))}>{item.title.get}</a>
def ajaxOnClickHandler(jsHandler: ()=>JsCmd) =
{
SHtml.onEvent( e => jsHandler()).toJsCmd+";return false;"
}
def editItemClickHandler(itemId: String): ()=>JsCmd = ()=>
{
trace.logAjaxComm("ExistingItem.Edit()")
JsCmds.SetHtml("LiId"+itemId, getEditableItem(promo) )
}
def getEditableItem(itemId) =
{
bind( ...
"promotitle" -> SHtml.text(editablePromo.get.promotitle.is,
(s:String) => {
trace.logUIDataManipulation("Saving new promo Title["+s+"]");
editablePromo.get.promotitle(s)
}, "id" -> "promotitle"),
"save" -> SHtml.button("Save", ()=> {})
)
}
Then when the user selects an Item, and the editable Item form is plugged in, there's "another" submit button that should ajax submit the form data for that item, and then swap back in the (now updated) nonEditable version of the data.
The problem for me is the submission. In addition to the Edit Item form above, I've got a ajaxified submit button on the "parent" non-editable list page to handle submitting some fields below the list. The Edit Item "save"-> binding adds a button, which should do (and in fact does) nothing for itself, but it does trigger the "Parent" submit button. And I route that submit to do the save of the Edit Item form.
The non-editable Item and the editable item code swaps fine, but changes made in the editable Item form is not saved, and I figured out that that was happening because the elements in the editable Item form are not being submitted at all, following is an example of a log message I don't see at all...
bind( ... "promotitle" -> SHtml.text(editablePromo.get.promotitle.is,
(s:String) => {
trace.logUIDataManipulation("Saving new promo Title["+s+"]");
editablePromo.get.promotitle(s)
}, "id" -> "promotitle")
)
In a normal ajaxified form, all element handlers are called (if there are changes to the field, I guess...) in order of rendering, with the submit/hidden elements' handlers being called last (if they're last in the bind list.
so finally, let's get around to my question:
if you're doing in-place editing like this, how do I manage 2 submit buttons (the one for the non-editable list page plus the additional one that gets added when editing an item)?
I'm sure I don't need to refresh the page, but I can't figure out how you'd do this with Ajax.
Maybe alternatively, the in-place editable form can be submitted as a non-submit ajax action, ie. somehow that doesn't trigger the parent submit?
For anyone tripping over this question, I figured I'd share the solution I eventually found...
1)The problem was that the submit (for AJAX this is the hidden html tag) happened before the editable Item's field handlers were called. So when the AJAX update that collapsed the editable Item back into just a non-Editable list item, the data hadn't yet been updated. So what was displayed in non-editable form didn't show the update, yet if I refreshed the page in the browser, the update had been saved to the database and now showed properly.
2)The reason for the mal-ordering is that Lift assigns each form tag's server-side handler an id (which are "monotonically increasing" with an additional string added to the end). That's fine until you do an ajax live-update of a form and add fields (as I did when I inserted the Editable Item fields). These newly-added fields were assigned server-side ids that came after the hidden field that got generated as part of the initial page rendering.
3)The solution was to explicitly shove the hidden field into a much higher id using S.formGroup. See here for more details...
The example from the last link below is as follows (and differs from mine in that it uses SHtml.submit, whereas I use SHtml.hidden). It adds the constant 1,000 to the submit button's server-side handler id:
"type=submit" #> S.formGroup(1000) {
SHtml.submit("Submit", process)
}
Discussion of a problem that is essentially the same as mine: https://groups.google.com/forum/#!topic/liftweb/MYJQeVlOYFM
Description of id assignment and S.formGroup under heading "Server side function order.":
https://www.assembla.com/spaces/liftweb/wiki/cool_tips
And lastly, linked to from the last link is some example code:
https://groups.google.com/forum/#!topic/liftweb/E9z7PVhogQw

cakePHP form with YUI text editor, not working

I am trying to integrate yui editor in a cakephp form
the editor is attached to the textarea, I tried the handleSubmit option and it didn't work, so I went trying manual. so- I've attached a listener to the onsubmit, which is working.. or not.
Editor initialization ( a copy-paste from yui site, only element named changed):
(function() {
//Setup some private variables
var Dom = YAHOO.util.Dom,
Event = YAHOO.util.Event;
//The SimpleEditor config
var myConfig = {
height: '300px',
width: '99%',
focusAtStart: true
};
//Now let's load the SimpleEditor..
var myEditor = new YAHOO.widget.SimpleEditor('ArticleContent', myConfig);
myEditor.render();
})();
Initialization works fine (I assume) since the editor now holds the real content of that record field.
The onsubmit listener function:
function setTextArea()
{
alert('s');
var dd = myEditor.saveHTML();
alert('d');
return false;
}
The first alert is working, so the event is handled.
However, the second alert never happens. the form - somehow - is submitted before it.
and, the content is not saved.
further checks shows that ANY call to myEditor [even alert(myEditor)] is submitting the form...
anyone? help? i
just a guess, but is any code in the 'saveHTML' function calling something that clashes with cakephp functions?
if this is the problem, you may be able to get around it by modifying the yui code function names (hacky i know, but unless there is some way to use a custom namespace for it i think you'd be stuck with it)
The best solution was to use tinyMCE....

Programmatically submitting a form while using AjaxForm

I wanted to find a way to upload a single file*, in the background, have it start automatically after file selection, and not require a flash uploader, so I am trying to use two great mechanisms (jQuery.Form and JQuery MultiFile) together. I haven't succeeded, but I'm pretty sure it's because I'm missing something fundamental.
Just using MultiFile, I define the form as follows...
<form id="photoForm" action="image.php" method="post" enctype="multipart/form-data">
The file input button is defined as...
<input id="photoButton" "name="sourceFile" class="photoButton max-1 accept-jpg" type="file">
And the Javascript is...
$('#photoButton').MultiFile({
afterFileSelect: function(){
document.getElementById("photoForm").submit();
}
});
This works perfectly. As soon as the user selects a single file, MultiFile submits the form to the server.
If instead of using MultiFile, as shown above, let's say I include a Submit button along with the JQuery Form plugin defined as follows...
var options = {
success: respondToUpload
};
$('#photoForm').ajaxForm(options);
... this also works perfectly. When the Submit button is clicked, the form is uploaded in the background.
What I don't know how to do is get these two to work together. If I use Javascript to submit the form (as shown in the MultiFile example above), the form is submitted but the JQuery.Form function is not called, so the form does not get submitted in the background.
I thought that maybe I needed to change the form registration as follows...
$('#photoForm').submit(function() {
$('#photoForm').ajaxForm(options);
});
...but that didn't solve the problem. The same is true when I tried .ajaxSubmit instead of .ajaxForm.
What am I missing?
BTW: I know it might sound strange to use MultiFile for single-file uploads, but the idea is that the number of files will be dynamic based on the user's account. So, I'm starting with one but the number changes depending on conditions.
The answer turns out to be embarrassingly simple.
Instead of programmatically submitting using...
document.getElementById("photoForm").submit();
... I used...
$("#photoForm").submit();
Also, since I only need to upload multiple files on occasion, I used a simpler technique...
1) The form is the same as my original...
<form id="photoForm" action="image.php" method="post" enctype="multipart/form-data">
2) The file input field is basically the same...
<input id="photoFile" "name="sourceFile" style="cursor:pointer;" type="file">
3) If the file input field changes, submit is executed...
$("#photoFile").change(function() {
$("#photoForm").submit();
});
4) The AjaxForm listener does its thing...
var options = {
success: respondToUpload
};
$('#photoForm').ajaxForm(options);

CollapsiblePanelExtender: Can I initiate collapse/expand from client-side javascript? (AJAX Control Toolkit)

The CollapsiblePanelExtender seems primarily designed to collapse/expand things in response to user mouse events. Is there also a good way to get the extender to collapse/expand things in response to client-side javascript?
In my particular case, I have a number of CollapsiblePanelExtenders (and their corresponding Panels) on a page, and I'm wondering if I could implement an "expand all panels" button by doing something like this strictly on the client side:
for each CollapsiblePanelExtender on this page, call somethingOrOther(extender)
I can implement this logic server-side instead if I did a full postback, but my page takes a long time to load, and so this doesn't seem like it would provide a very slick user experience. Thus I am interested in doing expand/collapse client-side.
It seems like this isn't a use case the AJAX Control Toolkit people had in mind, but I thought I'd check.
Write the following code in the OnClick event of Image/button
<asp:Image ID="img1" runat="server" OnClick="ExpandCollapse()"/>
function ExpandCollapse() {
$find("collapsibleBehavior1").set_Collapsed(true);
$find("collapsibleBehavior2").set_Collapsed(true);
}
Hope this helps!
I have a partly working solution now.
I followed Ian's suggestion and looked through the toolkit source. In CollapsiblePanelBehavior.debug.js, you can that expandPanel() is apparently intended as part of the public interface for the behavior. There's also a get_Collapsed(). The key to accessing these behaviors in javascript seems to be setting the BehaviorID property on your CollapsiblePanelExtender tags in ASP.NET.
I modified the repeater on my page so that the BehaviorIDs are predictible, along these lines:
<ajaxToolkit:CollapsiblePanelExtender
BehaviorID="<%#'collapsebehavior'+DataBinder.Eval(Container.DataItem,'id')%>"
ID="CollapsiblePanelExtender" runat="server" />
This results with behaviors named collapsebehavior1, collapsebehavior2, collapsebehavior3, etc..
With this done, I'm able to expand all the collapsible panels on the client as follows:
function expandAll() {
var i = 0;
while (true) {
i++;
var name = 'collapsebehavior' + i;
var theBehavior = $find(name);
if (theBehavior) {
var isCollapsed = theBehavior.get_Collapsed();
if (isCollapsed) {
theBehavior.expandPanel();
}
} else {
// No more more panels to examine
break;
}
}
}
I'm sure using $find in a loop like that is really inefficient, but that's what I have so far.
Also, it doesn't work on Firefox for some reason. (On FF only the first element expands, and then there's a Javascript error inside the Control Toolkit code.)
This will all seem extremely ugly to all you javascript pros. Maybe I'll clean things up later, or you can help me out.
You can also just toggle the panels to switch between collapsed/expanded states:
function toggle() {
var MenuCollapser = $find("name");
MenuCollapser.togglePanel();
}