caret position in an editable div (dartlang) - dart

I try to find the caret position in an editable div.
Also it should be nice to get the selected text in this div.
I try to assimilate this:
Editable Div Caret Position
But it dosen’t work.
I'll be happy to give any Idea.
Thanks in advance
Some code snippet
HTML
<a on-click="{{chooseMenu}}" which-menu="1">Menu 1</a>
Dart
void chooseMenu(Event event, var detail, var target) {
event.preventDefault();
event.stopPropagation();
Selection sel;
Range range;
sel = window.getSelection();
if(sel != null) {
range = sel.getRangeAt(0);
range.collapse(false);
}
currentSubMenu = int.parse(target.attributes['which-menu']);
}

This worked for me in Dartium.
The other code from the referenced SO question is probably to support other browsers.
Dart usually generates JS code that works in all supported browsers.
You have to test yourself if the generated JS really works in the browsers you want to support.
EDIT
We store the selection whenever it changes. Maybe there are better events but I couldn't find one.
But it worked fine for me.
We insert the new node at the previously stored selection.
// must be accessible by getRange, insertNodeAfterSelection
DivElement editable;
Range range;
// store reference to our contenteditable element
editable = (document.querySelector('#editable') as DivElement);
// subscribe selection change events
document.onSelectionChange.listen(getRange);
void insertNodeAfterSelection(Node node) {
range.collapse(false);
range.insertNode(node);
}
void getRange(Event e) {
if(document.activeElement != editable) { // save only selection changes on our contenteditable
return;
}
print(e);
Selection sel;
sel = window.getSelection();
if(sel != null) {
range = sel.getRangeAt(0);
}
}
You have to decide yourself where you put those code parts. I added some comments as support.
EDIT
using the mousedown event instead of click should help around this issue
Preserve text selection in contenteditable while interacting with jQuery UI Dialog and text input

Related

Flipswitch in lightswitch is going in an infinite loop

I got this piece of code for rendering and using Flipswitch as a custom control in lightswitch application.
function createBooleanSwitch(element, contentItem, trueText, falseText, optionalWidth) {
var $defaultWidth = '5.4em';
var $defaultFalseText = 'False';
var $defaultTrueText = 'False';
var $selectElement = $('<select data-role="slider"></select>').appendTo($(element));
if (falseText != null) {
$('<option value="false">' + falseText + '</option>').appendTo($selectElement);
}
else {
$('<option value="false">' + $defaultFalseText + '</option>').appendTo($selectElement);
}
if (trueText != null) {
$('<option value="true">' + trueText + '</option>').appendTo($selectElement);
}
else {
$('<option value="true">' + $defaultTrueText + '</option>').appendTo($selectElement);
}
// Now, after jQueryMobile has had a chance to process the
// new DOM addition, perform our own post-processing:
$(element).one('slideinit', function () {
var $flipSwitch = $('select', $(element));
// Set the initial value (using helper function below):
setFlipSwitchValue(contentItem.value);
// If the content item changes (perhaps due to another control being
// bound to the same content item, or if a change occurs programmatically),
// update the visual representation of the control:
contentItem.dataBind('value', setFlipSwitchValue);
// Conversely, whenver the user adjusts the flip-switch visually,
// update the underlying content item:
$flipSwitch.change(function () {
contentItem.value = ($flipSwitch.val() === 'true');
});
// To set the width of the slider to something different than the default,
// need to adjust the *generated* div that gets created right next to
// the original select element. DOM Explorer (F12 tools) is a big help here.
if (optionalWidth != null) {
$('.ui-slider-switch', $(element)).css('width', optionalWidth);
}
else {
$('.ui-slider-switch', $(element)).css('width', defaultWidth);
}
//===============================================================//
// Helper function to set the value of the flip-switch
// (used both during initialization, and for data-binding)
function setFlipSwitchValue(value) {
$flipSwitch.val((value) ? 'true' : 'false');
// Having updated the DOM value, refresh the visual representation as well
// (required for a slider control, as per jQueryMobile's documentation)
$flipSwitch.slider(); // Initializes the slider
$flipSwitch.slider('refresh');
// Because the flip switch has no concept of a "null" value
// (or anything other than true/false), ensure that the
// contentItem's value is in sync with the visual representation
contentItem.value = ($flipSwitch.val() === 'true');
}
});
}
This piece of code works fine. It renders the flipswitch on the screen. I am showing the data in an Edit screen, which is coming in a popup. Problem arises when I open that popup which contains the flipswitch and without changing any data on UI, I just try to close that popup screen. The IE hangs and it gives error saying that long script is running. When I debug the createBoolenaSwitch function, I came to know that it is going in infinite loop inside the function called setFlipSwitchValue(value){}
Why is this function getting called and this is going in an infinite loop?

CSS :hover iOS - on/off

I have searched the web for the past hour for a solution. I want to achieve the following hover-behavior on iOS.
1st tap -> hover on
2nd tap -> hover off
This will mainly be used to present images.
Because of the CSS tag I assume you want to do this in HTML.
This is almost impossible to do without javascript. You could add a click event handler that toggles the .hover class. For example (untested code, don't copy-paste ;)
element.addEventListener('click', function(evt) {
if (this.className == 'hover') {
this.className = 'hover';
} else {
this.className = '';
}
});
If you have other classes on the document it's probably easier to use a JS framework, e.g. jQuery (again untested):
jQuery(element).click(function(evt) {
jQuery(this).toggleClass('hover');
});

jQuery UI Accordion - does refresh method overwrites initialisation settings?

Currently I am working on a project for which I use the jQuery UI Accordion.
Therefore I initialise the accordion on an element by doing
<div id="accordion"></div>
$('#accordion').accordion({
collapsible: true,
active: false,
heightStyle: "content"
});
After init the accordion I append some data coming from an AJAX request. (depends on user interaction)
In a simplified jsfiddle - which does exact the same thing as the ajax call - you can see how this looks like.
So far it seems to be working quite well but there is one problem I face.
In my initialisation I say that I want all panels to be closed but after calling refresh on the accordion everything of those settings seems to be gone and one panel opens.
Note that I implemented jQuery UI v1.10.2 in my fiddle. Update notes say
The refresh method will now recognize panels that have been added or removed. This brings accordion in line with tabs and other widgets that parse the markup to find changes.
Well it does but why has it to "overwrite" the settings I defined for this accordion?
I also thought about the possibility that it might be wrong to create the accordion on an empty <div> so I tested it with a given entry and added some elements afterwards.
But the jsfiddle shows exactly the same results.
In a recent SO thread I found someone who basically does the same thing as I do but in his jsfiddle he faces the same "issue".
He adds a new panel and the first panel opens after the refresh.
My current solution for this issue is to destroy the accordion and recreate it each time there's new content for it.
But this seems quite rough to me and I thought the refresh method solves the need to destroy the accordion each time new content gets applied.
See the last jsfiddle
$(document).ready(function () {
//variable to show "new" content gets appended correctly
var foo = 1;
$('#clickMe').on('click', function () {
var data = '';
for (var i = 0; i < 3; i++) {
data += '<h3>title' + foo + '</h3><div>content</div>';
foo++;
}
if ($('#accordion').hasClass('ui-accordion')) {
$('#accordion').accordion('destroy');
}
$('#accordion').empty().append(data).accordion({
collapsible: true,
active: false,
heightStyle: "content"
});
});
});
Unfortunately it is not an option for me to change the content of the given 3 entries because the amount of panels varies.
So my questions are the one in the title and if this behaviour is wanted like that or if anybody faces the same problem?
For the explanation of this behaviour, have a look in the refresh() method of the jquery-ui accordion widget, the problem you are facing is at line 10 :
refresh: function() {
var options = this.options;
this._processPanels();
// was collapsed or no panel
if ((options.active === false && options.collapsible === true) || !this.headers.length) {
options.active = false;
this.active = $();
// active false only when collapsible is true
} if (options.active === false) {
this._activate(0); // <-- YOUR PROBLEM IS HERE
// was active, but active panel is gone
} else if (this.active.length && !$.contains(this.element[0], this.active[0])) {
// all remaining panel are disabled
if (this.headers.length === this.headers.find(".ui-state-disabled").length) {
options.active = false;
this.active = $();
// activate previous panel
} else {
this._activate(Math.max(0, options.active - 1));
}
// was active, active panel still exists
} else {
// make sure active index is correct
options.active = this.headers.index(this.active);
}
this._destroyIcons();
this._refresh();
}

Is there an event delegation in dart SDK?

Imagine, you want to listen to all clicks made to any anchor element on page. The anchors on page can be dynamically added/removed during the page lifetime and you want to register all of the click events, even on newly added.
Is there any way how to attach delegated event (like in jQuery) in Dart using its standard libraries?
In jQuery you can achieve this with element.on('click', '.selector', handler);.
You can now do that with ElementStream.matches like this :
document.body.onClick.matches('.selector').listen((Event event) {
print('Super cool!');
// The currently registered target for the event
// document.body
event.currentTarget;
// The element whose CSS selector matched
// .selector
event.matchingTarget;
// The target to which the event was originally dispatched
// the real element clicked under .selector
event.target;
});
Because I have found no viable solution, I have created package that enables delegation.
You can find it at
http://pub.dartlang.org/packages/delegate
Code is simple:
delegate(parent, condition, handler) {
return (event) {
var element = event.target;
while (element != parent && element != null) {
if (condition(element)) {
handler(event, element);
}
element = element.parent;
}
};
}
delegateOn(parent, String eventType, condition, handler) {
parent.on[eventType].listen(delegate(parent, condition, handler));
}

Nested Silverlight Datagrid - Row Details works great, but I want a button!

I'm using a silverlight 3 datagrid, and within it, I'm nesting related records in another control by using the rowdetails (visibilitymode = visiblewhenselected).
I really like how this works, but I'd much rather have the grid display the row details when a "+" button is pressed, much as a tree will expand when you click a node.
I tried programmatically defining the template by using resources like this:
<Grid.Resources>
<DataTemplate x:Key="EmptyTemplate">
<StackPanel>
<!--<TextBlock Text="Empty Template!!!" />-->
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="SongTemplate">
<StackPanel>
<AdminControls:ArtistSongControl x:Name="ArtistSongControl" />
</Stack>
</DataTemplate>
</Grid.Resources>
And in the grid's LoadingRowDetails event, I'd choose which template to set by:
e.Row.DetailsTemplate = (DataTemplate)LayoutRoot.Resources["SongTemplate"];
This sortof worked, but I found that I had problems with collapsing previous rows details template, and even crashed ie8 (not sure if that's related).
Basically, I really like how the silverlight 3 datagrid works, and even how the rowdetailstemplate stuff is implemented. I simply would like to defer loading any details until a row is expanded purposely (as a tree would be). All of the 3rd party grids seem to do this, and microsoft's is soooo close. Does anyone have any idea how to solve this one?
Thanks, Dennis
Dennis,
In case you haven't already found an answer to this, I wanted the same behavior and solved it by customizing the RowHeaderTemplate, which lets you throw a button in the header for each row. Then I implemented a handler for the button like so:
private void ToggleButton_Click(object sender, System.Windows.RoutedEventArgs e)
{
ToggleButton button = sender as ToggleButton;
DataGridRow row = button.GetVisualAncestorOfType<DataGridRow>();
if (button.IsChecked == true)
{
row.DetailsVisibility = Visibility.Visible;
//Hide any already expanded row. We only want one expanded at a time for simplicity and
//because it masks a virtualization bug in the datagrid.
if (_expandedRow != null)
_expandedRow.DetailsVisibility = Visibility.Collapsed;
_expandedRow = row;
}
else
{
row.DetailsVisibility = Visibility.Collapsed;
_expandedRow = null;
}
}
Note that GetVisualAncestorOfType<> is an extension method I've implemented to dig into the visual tree.
You'll also need to set the datagrid's HeadersVisibility property to Row or All
here is another way to achieve what you are trying to do:
In the DataGrid set up a LoadingRow Event like this:
<data:DataGrid LoadingRow="ItemsGrid_LoadingRow" .....
In the DataGrid create a Template Column which will contain a Button such as the following:
<data:DataGridTemplateColumn CellStyle="{StaticResource DataGridCellStyle1}" CanUserReorder="False">
<data:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button x:Name="ViewButton" Click="ToggleRowDetailsVisibility" Cursor="Hand" Content="View Details" />
</DataTemplate>
</data:DataGridTemplateColumn.CellTemplate>
</data:DataGridTemplateColumn>
In the LoadingRow Event locate the button that is (in this case) stored in the first column of the DataGrid, then store the current DataGridRow into the buttons Tag element
private void ItemsGrid_LoadingRow(object sender, DataGridRowEventArgs e)
{
var ViewButton = (Button)ItemsGrid.Columns[0].GetCellContent(e.Row).FindName("ViewButton");
ViewButton.Tag = e.Row;
}
In the Buttons EventHandler (in this case ToggleRowDetailsVisibility) we will extract the Row so that we can toggle its DetailsVisibility
In the LoadingRow Event locate the button that is (in this case) stored in the first column of the DataGrid, then store the current DataGridRow into the buttons Tag element
private void ToggleRowDetailsVisibility(object sender, RoutedEventArgs e)
{
var Button = sender as Button;
var Row = Button.Tag as DataGridRow;
if(Row != null)
{
if(Row.DetailsVisibility == Visibility.Collapsed)
{
Row.DetailsVisibility = Visibility.Visible;
//Hide any already expanded row. We only want one expanded at a time for simplicity and
//because it masks a virtualization bug in the datagrid.
if (CurrentlyExpandedRow != null)
{
CurrentlyExpandedRow.DetailsVisibility = Visibility.Collapsed;
}
CurrentlyExpandedRow = Row;
}
else
{
Row.DetailsVisibility = Visibility.Collapsed;
CurrentlyExpandedRow = null;
}
}
}
You will notice that "CurrentlyExpandedRow", this is a Global variable, of type DataGridRow, that we store the currently expanded row in, this allows us to close that Row when a new one is to be opened.
Hope this helps.
In addition to the answer uxrx provided here is the code for finding an ancestor
public static partial class Extensions
{
public static T FindAncestor<T>(DependencyObject obj) where T : DependencyObject
{
while (obj != null)
{
T o = obj as T;
if (o != null)
return o;
obj = VisualTreeHelper.GetParent(obj);
}
return null;
}
public static T FindAncestor<T>(this UIElement obj) where T : UIElement
{
return FindAncestor<T>((DependencyObject)obj);
}
}
For this:
DataGridRow row = button.GetVisualAncestorOfType<DataGridRow>();
We can use as:
HyperlinkButton button = sender as HyperlinkButton;
DataGridRow Row = DataGridRow.GetRowContainingElement(button)
I suggest you take a look at the RowVisibilityChanged event on the datagrid, basically when the row visibility changes to "Visible", then load the info for the row.

Resources