Javascript Rich Display WYSIWYG Component/Methodology - asp.net

quick back story--
I am working on ASP.Net based template editor that lets authors create text templates using Javascript inserted placeholder tags that will be filled in with dynamic text when the templates are used to display the final results.
For example the author might create a template like
The word [%12#add] was generated dynamically.
The application would eventually replace the tag with a dynamic word down the road (though it's not specifically relevant to this post)
The word foo was generated dynamically.
Depending on the circumstances, the template may be created in a text input, textarea or a modified version of the Ajax Control Toolkit HTML Editor. There might be 40 or more of these editable elements on the page, so using lots of stripped down or modified HTML editors would probably bog the page down too much.
The problem is that the tags such as [%12#add] are displayed inline with the user text and the result is confusing and aesthetically gross. The goal is parse the contens of the source element and when a tags such as [%12#add] are encountered, display something prettier and less cryptic to the user such as a stylable element or image wherever tags such as [%12#add] occur. The application still needs the template text with the tags on postback.
So the user might see
The word tag placeholder was generated dynamically.
but the original template would still be the value of the text input box
The word [%12#add] was generated dynamically.
It seems HTML editors like the ACT version and FckEditor accomplish this by rendering their output in an IFrame, but rather than kill myself trying to roll a lighter specialized version myself, I thought I'd ask if anyone knows of an existing free component or approach that has already tackled this.
With good reason, I don't think S.O. allows HTML formatting, but the bold "tag placeholder" above would ideally be something like tag placeholder.

I think CKEditor might be your best bet. I recently wrote a plugin for it that kept placeholders in the editable content for chunks of content that the user couldn't edit directly. A question I asked may help, particularly the comments to the accepted answer: Update editor content immediately before save in CKEditor plug-in.
The recommendation to me was to look at how object tags (e.g. as used to embed Flash movies) are handled, and from that I was able to proceed fairly quickly. Be aware though that CKEditor is not well documented for plugin developers, so you may often have to resort to looking at the source code.

Final model solution in case someone in the same situation needs a boost.
aspx page:
<div>
<asp:TextBox runat="server" ID="txtTest" TextMode="MultiLine" CssClass="Over" />
<br />
Add CKEdit
<br />
Add Tag
<br />
<asp:Button ID="btnSubmit" runat="server" Text="Submit" onclick="btnSubmit_Click" />
</div>
<script type= "text/javascript" >
<!--
function ckTest(el) {
var tinyTool = {
toolbar:
[
['Bold', 'Italic', 'UIColor'], ['Styles', 'Format', 'Font', 'FontSize']
]
};
//Note: config.htmlEncodeOutput = true; to work with ASP.NET, see postback for decoding input
var editor = CKEDITOR.replace(el);//, tinyTool);
editor.addCss('.aux1 { background-color: #FFE0C0; border: solid 1px #17659E; }');
}
function insertTag(id, tag, display) {
var e = CKEDITOR.instances[id];
if (e) {
//Storing in comments does not work. stripped out when using insertHtml. Workaround?
//e.insertHtml("<span class='aux1'>" + display + "<!--" + tag + "--></span>");
//Kludge: fake attribute
e.insertHtml("<span class='aux1' tag='" + tag +"'>" + display + "</span> ");
}
}
-->
</script>
-->
</script>
CodeBehind:
protected void btnSubmit_Click(object sender, EventArgs e)
{
//Note: CKEditor converts single to double quotes in insertHtml
Regex regHiddenTag = new Regex(#"<span\sclass=""\w+""\stag=""(\[%[0-9]{1,2}_[TR]\])"">\w+</span>");
//Note: config.htmlEncodeOutput = true;
string encoded = txtTest.Text
.Replace("<", "<").Replace(">", ">").Replace("&", "&");
//TODO: Use AntiXss Library that I have to thwart bad HTML
string extractedTag = regHiddenTag.Match(encoded).Groups[1].Value;
//store to DB
string template = regHiddenTag.Replace(
encoded,
extractedTag);
//repopulate
string finalText = template.Replace(extractedTag, "foo");
}

Related

select2 AJAX control disturbs tab ordering

I am using select2 on dropdownlist of asp.net. The code is as follows:
<script type="text/javascript" src="js/select2.min.js"></script>
<link type="text/css" rel="Stylesheet" href="css/select2.css" />
var v = /* get the select control */
v.select2();
The problem is, once select2() function is called, tab ordering stops working. Therefore, when on the dropdownlist tab key is pressed, focus do not move to the control having next highest tabindex but move seemingly randomly to some other control.
Commenting the line where the function is called solve this problem but I need the filtering. I have tried some of the other techniques of filtering discussed here but they are too complicated. Select2 is very simple and useful because all you have to do is include the JS and CSS files and call the function.
How can I solve this ordering problem? Alternatively, is there another filtering option as easy to use as select2 that would help me?
After a few hours of struggle, I have solved the problem. It turns out that the select2 AJAX control do destroy the tab order if the tab is pressed as soon as it gets focus, that is, when nothing is typed in it. It does not, however, destroy tab ordering if some text is typed.
The internal structure of select2's auto-generated HTML is like following:
<div class="select2-container">
<a class="select2-choice">
<span</span>
<abbr class="select2-search-choice-close" />
<div> <b></b> </div>
</a>
<div class="select2-drop select2-offscreen">
<div class="select2-search">
<input class="select2-input select2-focused" tabIndex=<somevalue> />
</div>
<ul class="select2-results></ul>
</div>
</div>
If some text is typed in the HTML select control, then tab ordering is working correctly, if no text is typed then tab order is destroyed. I have used document.activeElement in firebug to find focused control in both cases. In case of no text the anchor element has focus, and in case of text typed the HTML input element has focus.
As shown above, while select2.js correctly set tabIndex property of HTML input element, it does not of the anchor element.
Solution
Just add the following line at the position specified further below in select2.js:
this.container.find("a.select2-choice").attr("tabIndex", this.opts.element.attr("tabIndex"));
Add the line after:
this.opts.element.data("select2", this).hide().after(this.container);
this.container.data("select2", this);
this.dropdown = this.container.find(".select2-drop");
this.dropdown.css(evaluate(opts.dropdownCss));
this.dropdown.addClass(evaluate(opts.dropdownCssClass));
this.dropdown.data("select2", this);
this.results = results = this.container.find(resultsSelector);
this.search = search = this.container.find("input.select2-input");
and before:
search.attr("tabIndex", this.opts.element.attr("tabIndex"));
this.resultsPage = 0;
this.context = null;
// initialize the container
this.initContainer();
this.initContainerWidth();
installFilteredMouseMove(this.results);
this.dropdown.delegate(resultsSelector, "mousemove-filtered", this.bind(this.highlightUnderEvent));
installDebouncedScroll(80, this.results);
this.dropdown.delegate(resultsSelector, "scroll-debounced", this.bind(this.loadMoreIfNeeded));
So it becomes:
this.results = results = this.container.find(resultsSelector);
this.search = search = this.container.find("input.select2-input");
this.container.find("a.select2-choice").attr("tabIndex", this.opts.element.attr("tabIndex")); /* atif */
search.attr("tabIndex", this.opts.element.attr("tabIndex"));
this.resultsPage = 0;
this.context = null;
Make this change in select2.js. Obviously, you need to use the full js version, not the min version.
All you have to do is add one line, stated above. This would become line no#504 in VS2008 if done correctly.

How can I add a random number to an ASP.NET menu item url on each click

One step to prevent caching (in addition to adding the appropriate headers etc..) is to add a random number to the end of my URLs.
I'm using an ASP.NET menu and would like to add a random number to each menu item's navigate URL as it is clicked.
I can do this in the MenuItemDataBound event, but haven't had much luck doing the same with the MenuItemClicked Event.
Answer (can't answer my own question for 8 hours, and I don't have time to wait that long so here's my server side solution.)
To do this server side, I've had to remove the sitemap and the databinding from the menu.
I simply added all of the items from the sitemap as menuitems to the items collection in the menu markup removing the url property. The key here is removing the url property.
<asp:menu>
<items>
<asp:menuitem Text="Home" ToolTip="Go Home" Selectable="True" />
</items>
</asp:menu>
Then in your code behind you can handle the MenuItemClicked event (which should now fire, because there is no longer a navigateurl in the markup).
In the MenuItemClicked event codebehind I simply do the following:
string TimeStamp = DateTime.Now.ToString("yyyyMMddHHmmssfffffff");
// get iframe control - must have 'runat=server' attribute
HTMLControl display = CType(this.FindControl("display"), HTMLControl);
// dispatch menuitem
switch (e.item.valuepath)
{
case "Home":
display.attributes("src") = "home.aspx?=" + TimeStamp()
break;
.
.
.
}
This is the server side solution with an iframe.
I don't know if you're considering client-side URL manipulation as an option, but running this little bit of JavaScript on each page load would give you the behavior you're looking for by appending a timestamp to each of the links. You can modify it to target links in a specific area/div of the site, but this example will change them all:
<!-- include the jQuery library -->
<script type="text/javascript">
$(function(){
var time = new Date().getTime();
$('a').each(function() {
var append = (this.href.indexOf('?') > -1 ? '&' : '?');
$(this).attr('href', this.href + append + 't=' + time.toString());
});
});
</script>
Since every time the page loads the timestamp will be different, you should always get a unique set of links.
EDIT Here's a working jsFiddle demoing the behavior: http://jsfiddle.net/2HzqU/2/
I don't think that's the best solution. Have you tried using something like this:
HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.NoCache);
HttpContext.Current.Response.Cache.SetNoStore();

Use naming convention to apply class if label contains text

I'm new to jquery, and sorry if this question is asked before (could find exactly what I was looking for)
I would like to implement the following convetion: If I create a error label with the name '_lblError' like this:
<div>
<asp:label ID="_lblError" text="this is a error msg"/>
</div>
classA or classB would be applied to it depending on a empty string or not in the 'text' parameter of the label.
I also would like to place this code in a external js file that would be used throughout the app.
Thanks for any help!
To start with, you probably need to give the label a css class that can be used for selection:
<asp:Label Id="_lblError" Text="This is the error message"
CssClass="ThisIsWhatWeWillllWorkWith" />
This will probably output something like
<span id="ct100__lblError" class="ThisIsWhatWeWillWorkWith">
This is the error message.
</span>
You can now select the label in jQuery using the class as selector, and add class A or B depending on whether the .text() property is empty or not.
$(function() {
$('.ThisIsWhatWeWillWorkWith').each(function() {
if($(this).text() == '') { $(this).addClass('ClassA'); }
else { $(this).addClass('ClassB'); }
});
});
All code is provided as is, with no guarantees of working without modification. But you get the general idea of how to solve the problem...
EDIT: In response to your comment, here's a way to do it without adding a css class to the label. Instead of using an <asp:Label> tag for the error message, wrap a literal in a tag you hard-code on your page:
<span class="ThisIsWhatWeWillWorkWith"><asp:Literal ID="__ltlError" Text="This is the error message.</asp:Literal></span>
Another, perhaps more elegant way, would be to create your own custom label, and use that instead.
public class ErrorLabel : System.Web.UI.WebControls.Label
{
public ErrorLabel() {
this.CssClass = "ThisIsWhatWeWillWorkWith";
}
}
You then put the error message on your page with the following line:
<asp:ErrorLabel ID="__lblError" Text="This is the error message" />
Again, not sure if the above code will work as is. But again, you get the idea of what to do...
If the idea is to provide say, have different font and/or background if there is an error and display nothing if there is no text in the error label then you could make the control a literal instead of a label. The literal control does not create a control with no text (MSDN doc)

Way to update css properties on-the-fly based on form content?

I have a form on a website, in which one of the inputs is to be used to enter hexadecimal colour codes to be entered into a database.
Is there any way for the page to dynamically update itself so that if the user changes the value from "000000" to "ffffff", the "colour" CSS property of the input box will change immediately, without a page reload?
Not without Javascript.
With Javascript, however...
<input type='text' name='color' id='color'>
And then:
var color = document.getElementById('color');
color.onchange = function() {
color.style.color = '#' + this.value;
}
If you are going to go the Javascript route, though, you might as well go all out and give them a color picker. There are plenty of good ones.
CSS properties have corresponding entries in the HTML DOM, which can be modified through Javascript.
This list is somewhat out of date, but it gives you some common CSS pieces and their corresponding DOM property names.
Granted, a JS lib like like jQuery makes this easier...
You can use Javascript to achieve that.
As an example:
Your HTML:
<input type="text" id="test" />
Your JS:
var test = document.getElementById('test');
test.onchange = function(){
test.style.color = this.value;
};
But this doesn't check the user's input (So you would have to extend it).

Forcing client ids in ASP.NET

I know that in the next version of ASP.NET we'll finally be able to set the clientids on System.Web controls without the framework doing it for us in a quasi-intelligent way e.g:
id="ctl00__loginStatus__profileButton"
Does anyone know a good method in the meantime to force the above id to something like
id="profileButton"
The main reason for this is manipulation of the clientids in jQuery when dynamically adding controls to a page. The problem I can see is changing the ids will break the Viewstate?
You have to use the ClientIDMode attribute:
<asp:xxxx ID="fileselect" runat="server" ClientIDMode="Static"/>
What I tend to do is dynamically generate javascript methods which handle this. You can do this in markup or code behind so for example:
<script language="javascript" type="text/javascript">
function doXYZ()
{
$("#" + getListBoxId()).css(...)
}
function getListBoxId()
{
return "<%=this.myListBox.ClientId>";
}
</script>
You can also build the functions in the code behind and register them.
EDIT
A couple months ago I needed to fix the id of some server controls, I managed to hack it in and I described my method here here.
Basically you need put the controls inside a naming container like a user control, and then override a couple of properties which prevents the child controls from getting their uniqueid.
The performance isn't great, but you can use this selector syntax to match messy ClientIDs:
$("[id$='_profileButton']")
That matches any element ending in _profileButton. Adding the leading underscore ensures that you're matching the desired element and not another element that ends in the substring "profileButton" (e.g. "myprofileButton").
Since it has to iterate over the entire DOM, the performance can be poor if you use it in a loop or several times at once. If you don't overuse it, the performance impact is not very significant.
Another way would be to wrap your control with a div or span with a static id, then access the control through that.
E.g.
<span id="mySpan">
<asp:TextBox id="txtTest" runat="server" />
</span>
You could then target input tags inside MySpan. (though I agree it would be nice to be able to specify a nice name, provided you could handle the naming conflicts...)
I have often run in to this "problem" while developing in asp.net webforms. In most cases I tend to use the css class of the element.
jQuery(".My .Container12")
Before starting to manipulate the id:s, perhaps that is a way you can handle it aswell? It's a simple solution.
There is another solution not mentioned which is to subclass the ASP.NET controls and force the IDs:
public class MyCheckBox : CheckBox
{
public string ForcedId { get;set; }
public override string ID
{
get
{
if (!string.IsNullOrEmpty(ForcedId))
return ForcedId;
else
return base.ID;
}
set
{
base.ID = value;
}
}
public override string ClientID
{
get
{
return ID;
}
}
}
Then use this where you know the IDs will never clash:
<mytag:MyCheckBox ForcedId="_myCheckbox" runat="server" />
If you are using lists you will need to write a ListControlAdapter, and also adapters for each type of list you're using (dropdown,checkbox,radiobutton,listbox). Alternatively cross your legs and wait for .NET 4.0.

Resources