Title Back Colour Keyoti Title Line Title Curve
Blue Box Top

Gridview and Repeater extremely slow to load with spellchecker - RapidSpell Web ASP.NET - Forum

Welcome Guest Search | Active Topics | Log In | Register

2 Pages 12>
Options
drudlin
#1 Posted : Wednesday, October 14, 2015 9:22:34 PM
Rank: Member

Groups: Registered

Joined: 7/7/2009
Posts: 12
In our asp.net 3.5 website we have a gridview and repeater both with spellchecking implemented. These two controls are placed inside a Ajax Tabcontainer, inside an UpdatePanel loaded in an ASCX page by a Master page.

Where the records in these controls become substantial i.e. 500 records or more, the load speed of the controls is hugely increased with the spellchecker and more than halved when the spellchecker is removed.

I am wondering if our implementation is incorrect.

So in the Repeater for example we have:

a) the RapidSpellWebMultiple control placed above the Repeater

b) the Repeater has three textboxes per row and a couple of radio buttons.

c) beside each of the three text boxes we have a ReapidSpellWebLauncher like so:

<asp:TextBox ID="txtExitLevel" runat="server" Font-Size="11px" Height="60px"
Text='<%# Container.DataItem("ExitLev")%>' TextMode="MultiLine"
Width="140px" CssClass="normalText" ></asp:TextBox>

<RapidSpellWeb:RapidSpellWebLauncher ID="rsWebLauncherExit" runat="server"
ConsiderationRange="500" FieldDisplayText='Target level' RapidSpellWebPage="~/helper/popup.aspx"
TextComponentName="txtExitLevel" ShowButton="False" Style="z-index: 106; left: 667px; position: absolute; top: 276px"
CreatePopUpWindow="False" Validator="True"
SSLFriendly="true" RSMultipleID="RapidSpellWebMultiple">
<Button ID="Button7" runat="server" BorderStyle="NotSet" Enabled="True" TabIndex="0"
type="button" Value="Check Spelling" />
</RapidSpellWeb:RapidSpellWebLauncher>


Finally, there is nothing in the code behond that appears to be influencing the speed. Quite simply, when the ReapidSpellWebLauncher is removed from each row of the Repeater (or Gridview) the load speed of the control is hugely improved.

Can anybody suggest anything that we doing wrong? Is there a better way of implementing spellchecking for a Repeater or Gridview?

Many thanks.
Dave
Jim
#2 Posted : Wednesday, October 14, 2015 11:21:44 PM
Rank: Advanced Member

Groups: Administrators, Registered

Joined: 8/13/2004
Posts: 2,667
Location: Canada
Hi Dave, are you saying there are 1500 RapidSpellWebLauncher controls on the page, or is just one row editable at a time?

Best
Jim
-your feedback is helpful to other users, thank you!


drudlin
#3 Posted : Thursday, October 15, 2015 8:29:20 AM
Rank: Member

Groups: Registered

Joined: 7/7/2009
Posts: 12
Yes Jim, all 1500 records are loaded and editable, each record with its own RapidSpellWebLauncher of course. The Repeater is a fixed height with a scroll bar and there is no paging. Spellchecking for the whole record set is done by the single RapidSpellWebMultiple control just above the Repeater. As I say, it appears to be the rendering of the RapidSpellWebLaunchers that is causing the extremely slow loading of the Repeater; once I remove it from the row, the whole thing loads much much faster.
Dave
Jim
#4 Posted : Thursday, October 15, 2015 5:21:00 PM
Rank: Advanced Member

Groups: Administrators, Registered

Joined: 8/13/2004
Posts: 2,667
Location: Canada
Thanks - so the thing about the Server Controls is that they each have their own chunk of Javascript/HTML, and there isn't much sharing of common code. So multiply it by 1,500 and you have a lot of code to download to the browser.

In v4 we added the ability to do it from Javascript, which is not only simpler, but also more efficient as there is no duplication of code.

https://keyoti.com/produ...mples/JSmode/Dialog.htm

You add

<script src="RapidSpell-DIALOG.js"></script>

to the page, and it automatically finds all textboxes to spell check, unless you want to explicitly block/check certain textboxes.

You trigger the spell check with

<input type="button" onclick="rapidSpell.dialog_spellCheck()" value="Spell Check" />


there should be next to no overhead with regard to the page loading.

Best
Jim
-your feedback is helpful to other users, thank you!


drudlin
#5 Posted : Friday, October 16, 2015 2:08:47 PM
Rank: Member

Groups: Registered

Joined: 7/7/2009
Posts: 12
Thank you Jim. Do you happen to have a complete example of spellchecking for an asp.net Repeater with the spellcheck button placed outside of the Repeater? This javascript method is quite different to how we have previously implemented it.

As I described previously, the repeater is placed inside a Ajax Tabcontainer, inside an UpdatePanel loaded in an ASCX page by a Master page.

We've added the scripts to the top of th ASCX page:
<script src="../../../../Scripts/Keyoti_RapidSpell_Web_Common/RapidSpell-AYT.js"></script>
<script src="../../../../Scripts/Keyoti_RapidSpell_Web_Common/RapidSpell-DIALOG.js"></script>

and the input button, outside of the Repeater:
<input type="button" onclick="rapidSpell.dialog_spellCheck(true, ['txtEntryLevel', 'txtTargetLevel', 'txtExitLevel'])" value="Spell check" style="width: 90px; font-size: 12px" class="Button" />

I'm sure there is a lot more to be done, for example decalarations of user dictionary paths (in code behind as we previosuly did?) - so an example would be good.

Dave
drudlin
#6 Posted : Friday, October 16, 2015 3:13:35 PM
Rank: Member

Groups: Registered

Joined: 7/7/2009
Posts: 12
Sorry Jim - ignore my post of 2:08PM today, I've just found the version 4 Repeater demo. I'll go through that and get back to you if I have any problems. Thank you for your help so far.
Dave
Jim
#7 Posted : Friday, October 16, 2015 3:57:13 PM
Rank: Advanced Member

Groups: Administrators, Registered

Joined: 8/13/2004
Posts: 2,667
Location: Canada
No problem. Btw, I wouldn't add the script import for RapidSpell-AYT.js to your page because it will trigger 1500 as-you-type textboxes, which are actually IFRAMES, probably too heavy for the page.
-your feedback is helpful to other users, thank you!


drudlin
#8 Posted : Friday, October 16, 2015 4:22:16 PM
Rank: Member

Groups: Registered

Joined: 7/7/2009
Posts: 12
Thanks Jim. I removed the AYT.js.
However, just a first attempt, but running the spellchecker, it finishes immediately with the message "The spelling check is complete". I'm suspecting there may be an issue with the ID of the textbox parameters of the input button. I have added nothing to the vb code behind and haven't yet implemented userdictionary paths. Here's what I have (Repeater is simplified):

<script src="../../../../Scripts/Keyoti_RapidSpell_Web_Common/RapidSpell-DIALOG.js"></script>


<script type="text/javascript">
rapidSpell.setParameterValue("default", "ConsiderationRange", 500);

//client side function to highlight text box being checked, called when spell checker starts a
//new text box.
function Ondialog_startedcheckingtextbox(src, textBox) {
debugger;
//highlight current text box
textBox.style.border = "2px solid blue";
}

function Ondialog_finishedcheckingtextbox(src, textBox) {
//remove highlights on all text boxes
for (var i = 0; i < rsw_rapidSpellControls.length; i++) {
textBox = document.getElementById(rsTCInt[rsw_rapidSpellControls[i]].tbName);
textBox.style.borderWidth = '';
textBox.style.borderStyle = '';
textBox.style.borderColor = '';
}
}


//- Called when spell checking is finished
function NotifyDone(src, tb, complete) {
if (complete) {
alert("Spellcheck complete.");
//do whatever else
} else {
if (!confirm("You didn't finish spell checking, are you sure you want to cancel the spell check?")) {
//restart the spell checker - see userguide for this function's pattern
setTimeout("rapidSpell.dialog_spellCheck(true, tb)", 1); //Let the window close before reopening it
}
}
}

//- Called when corrections are made
function NotifyCorrection(src, tb, wordStart, oldWord, newWord) {
//write changes to correctionBox
document.getElementById("correctionBox").setAttribute("value", "'" + oldWord + "' at pos. " + wordStart + " changed to '" + newWord + "'");
}


//this function is for SPELLCHECKING changes to Entry, Target and Exit levels
function NotifyCorrection(src, tbName, wordStart, oldWord, newWord){
var n = tbName.indexOf("txt");
var res = tbName.substring(0, n + 3);
var hfName = res + "_flagChange"
document.getElementById(hfName).value = '1';
document.getElementById('<%= hdnOutcomeChanged.ClientID%>').value = 1;
}

rapidSpell.addEventListener('dialog_startedcheckingtextbox', Ondialog_startedcheckingtextbox);
rapidSpell.addEventListener('dialog_finishedcheckingtextbox', Ondialog_finishedcheckingtextbox);
rapidSpell.addEventListener('dialog_finishedcheckingtextbox', NotifyDone);
rapidSpell.addEventListener('dialog_correction', NotifyCorrection);
</script>


<input type="button" onclick="rapidSpell.dialog_spellCheck(true, ['txtEntryLevel', 'txtTargetLevel', 'txtExitLevel'])" value="Spell check" style="width: 90px; font-size: 12px" class="Button" />


<asp:Repeater ID="rptrEntryLevel" runat="server">
<ItemTemplate>
<td>
<asp:TextBox ID="txtEntryLevel" runat="server" Font-Size="11px" Height="60px"
Text='<%# Container.DataItem("EntryLev")%>' TextMode="MultiLine"
Width="140px" CssClass="normalText"></asp:TextBox>
</td>

<td>
<asp:TextBox ID="txtTargetLevel" runat="server" Font-Size="11px" Height="60px"
Text='<%# Container.DataItem("TargetLev")%>' TextMode="MultiLine"
Width="140px" CssClass="normalText"></asp:TextBox>
</td>

<td>
<asp:TextBox ID="txtExitLevel" runat="server" Font-Size="11px" Height="60px"
Text='<%# Container.DataItem("ExitLev")%>' TextMode="MultiLine"
Width="140px" CssClass="normalText"></asp:TextBox>
</td>
</tr>
</ItemTemplate>
</asp:Repeater>
Dave
Jim
#9 Posted : Friday, October 16, 2015 6:00:35 PM
Rank: Advanced Member

Groups: Administrators, Registered

Joined: 8/13/2004
Posts: 2,667
Location: Canada
You're right Dave, it is the ID of the textboxes that is the problem.

rapidSpell.dialog_spellCheck(true, ['txtEntryLevel', 'txtTargetLevel', 'txtExitLevel'])

It needs txtEntryLevel.ClientID. I think you can only get that in the ItemCreated event of the Repeater.

But, do you want to specify certain textboxes to check? If you don't pass any IDs it will check every textbox (and it's easier to mark certain textboxes not to be spell checked).

If you wanted to just spell check a group of textboxes (like a row), then a different approach is to identify the textbox IDs via Javascript....

Jim

-your feedback is helpful to other users, thank you!


drudlin
#10 Posted : Friday, October 16, 2015 7:24:06 PM
Rank: Member

Groups: Registered

Joined: 7/7/2009
Posts: 12
Thanks Jim. So we removed references to the Repeater textboxes in the input button as you suggested:
<input type="button" onclick="rapidSpell.dialog_spellCheck()" value="Spell check" style="width: 90px; font-size: 12px" class="Button" />

...and then blocked checking of the other textboxes that it appeared to want to check (which it was checking successfully actually). (Interestingly it didn't seem to want to check several small SingleLine textboxes on the TabContainer)
rapidSpell.setParameterValue("default", "ConsiderationRange", 500);

rapidSpell.dialog_popupURL = " ~/helper/popup.aspx";

rapidSpell.dialog_ignoreTextBoxIds[rapidSpell.dialog_ignoreTextBoxIds.length] = '<%= txtAims.ClientID %>';
rapidSpell.dialog_ignoreTextBoxIds[rapidSpell.dialog_ignoreTextBoxIds.length] = '<%= txtMon.ClientID %>';
rapidSpell.dialog_ignoreTextBoxIds[rapidSpell.dialog_ignoreTextBoxIds.length] = '<%= txtSession.ClientID %>';
rapidSpell.dialog_ignoreTextBoxIds[rapidSpell.dialog_ignoreTextBoxIds.length] = '<%= txtAssess.ClientID %>';

..so it should now just check the Repeater but when we run the spellchecker, it opens the popup dialog, and the dialog box is blank. The very first textbox in the first repeater row is highlighted in blue. And that's it.

Any other suggestions as to what we're doing wrong?
Dave
Jim
#11 Posted : Friday, October 16, 2015 8:13:55 PM
Rank: Advanced Member

Groups: Administrators, Registered

Joined: 8/13/2004
Posts: 2,667
Location: Canada
Sorry I can't think of why that would happen. Can you repro in a cut down page that you can post here, so I can run it and fix?
-your feedback is helpful to other users, thank you!


drudlin
#12 Posted : Friday, October 16, 2015 9:00:09 PM
Rank: Member

Groups: Registered

Joined: 7/7/2009
Posts: 12
Before I do that (quite a big job!) - you mentioned previously that: "If you wanted to just spell check a group of textboxes (like a row), then a different approach is to identify the textbox IDs via Javascript...."
So we do actually want to check three neighbouring textboxes in each Repeater row. Do you have an example of how to identify the textbox IDs via Javascript within the spellchecking script?
Dave
Jim
#13 Posted : Saturday, October 17, 2015 10:44:56 PM
Rank: Advanced Member

Groups: Administrators, Registered

Joined: 8/13/2004
Posts: 2,667
Location: Canada
You can get the IDs with a function like this

Code:

function getContainedTextFields(element)
var ids = [];

var children = element.childNodes;
for (var j=0; j < children.length; j++)
{
if (children[j].tagName == "TEXTAREA" || (children[j].tagName == "INPUT" && children[j].getAttribute('type').toLowerCase()=="text"))
{
ids[ids.length] = children[j].id;
}
}

return ids;


so call that function, passing an element that wraps a row, and then pass the IDs to the spell checker, eg.

Code:

<input type="button" onclick="els = getContainedTextFields(document.getElementById('row2')); rapidSpell.dialog_spellCheck(true, els)" value="Spell check" style="width: 90px; font-size: 12px" class="Button" />




where 'row2' would be the ID of an element that wraps the row in question.
-your feedback is helpful to other users, thank you!


drudlin
#14 Posted : Sunday, October 18, 2015 7:06:20 PM
Rank: Member

Groups: Registered

Joined: 7/7/2009
Posts: 12
Many thanks Jim. We'll try that.
So I guess the idea is to wrap each row in an element with an ID like a Panel or UpdatePanel rather than get the row ID (ItemIndex)?
Dave
Jim
#15 Posted : Monday, October 19, 2015 3:51:48 AM
Rank: Advanced Member

Groups: Administrators, Registered

Joined: 8/13/2004
Posts: 2,667
Location: Canada
Dave, yes you want any element that wraps a row. I expect that there's already a wrapping element output by the repeater/grid. Otherwise yes putting a Panel in there would help, just remember it's the ClientID you need to use...

Jim
-your feedback is helpful to other users, thank you!


drudlin
#16 Posted : Tuesday, October 27, 2015 8:51:21 PM
Rank: Member

Groups: Registered

Joined: 7/7/2009
Posts: 12
Thanks Jim. We've managed to get the ClientIDs via a bit of JQuery script.
However, when we click the spellcheck button, a modal popup appears but it is completely empty. Also the first textbox in the Repeater is highlighted in blue as it should be. Basically the popup is not being loaded with any content and I cannot see why. We've copied all the Keyoti_RapidSpell_Web_Common scripts into a folder in the project. Any ideas?

Here is the relevant code on our page with simplified Repeater:

<script type="text/javascript" src="../../../../Scripts/Keyoti_RapidSpell_Web_Common/RapidSpell-DIALOG.js"></script>

<script type="text/javascript">
function getContainedTextFields() {

var ids = [];
var numberOfBoxesToCheck = $('.cssRptrTB').length;
for (i = 0; i < numberOfBoxesToCheck; i++) {
var txbID = $('.cssRptrTB').eq(i).attr('ID');
ids[ids.length] = txbID;
}
return ids;
}

</script>


<script type="text/javascript">
rapidSpell.setParameterValue("default", "ConsiderationRange", 500);

function Ondialog_startedcheckingtextbox(src, textBox) {
// debugger;
//highlight current text box
textBox.style.border = "2px solid blue";
}

function Ondialog_finishedcheckingtextbox(src, textBox) {
//remove highlights on all text boxes
for (var i = 0; i < rsw_rapidSpellControls.length; i++) {
textBox = document.getElementById(rsTCInt[rsw_rapidSpellControls[i]].tbName);
textBox.style.borderWidth = '';
textBox.style.borderStyle = '';
textBox.style.borderColor = '';
}
}


function NotifyCorrection(src, tb, wordStart, oldWord, newWord){
var n = tb.indexOf("txt");
var res = tb.substring(0, n + 3);
var hfName = res + "_flagChange"
document.getElementById(hfName).value = '1';
document.getElementById('<%= hdnOutcomeChanged.ClientID%>').value = 1;
}


rapidSpell.addEventListener('dialog_startedcheckingtextbox', Ondialog_startedcheckingtextbox);
rapidSpell.addEventListener('dialog_finishedcheckingtextbox', Ondialog_finishedcheckingtextbox);
rapidSpell.addEventListener('dialog_correction', NotifyCorrection);


</script>




<input type="button" id='dialogBT' onclick="els = getContainedTextFields(); rapidSpell.dialog_spellCheck(true, els)"
value="Spell check" style="width: 90px; font-size: 12px" class="Button" />


<asp:Repeater ID="rptrLevels" runat="server">
<ItemTemplate>
<tr style="vertical-align: top" id="rptrDataRow">
<td>
<asp:TextBox ID="txtEntry" runat="server" Font-Size="11px" Height="60px"
Text='<%# Container.DataItem("EntryLev")%>' TextMode="MultiLine" ToolTip="255 characters limit"
Width="140px" CssClass="cssRptrTB"></asp:TextBox>
</td>


<td>
<asp:TextBox ID="txtTarget" runat="server" Font-Size="11px" Height="60px"
Text='<%# Container.DataItem("EntryLev")%>' TextMode="MultiLine" ToolTip="255 characters limit"
Width="140px" CssClass="cssRptrTB"></asp:TextBox>
</td>
</tr>
</ItemTemplate>
</asp:Repeater>


Dave
Jim
#17 Posted : Wednesday, October 28, 2015 5:18:36 PM
Rank: Advanced Member

Groups: Administrators, Registered

Joined: 8/13/2004
Posts: 2,667
Location: Canada
Dave, I tried the following and it worked fine - can you try and see if you can get it to break? I modified the dataitem a tiny bit but doubt it matters.

WebForm1.aspx

Code:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm1.aspx.cs" Inherits="RSWEB_JS.SupportIssues.RepeaterDialog.WebForm1" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
    <script src="../../ScriptsX/jquery-1.4.1.js"></script>
    <script src="../../ScriptsX/RapidSpell-DIALOG.js"></script>
    <script type="text/javascript">
        function getContainedTextFields() {

            var ids = [];
            var numberOfBoxesToCheck = $('.cssRptrTB').length;
            for (i = 0; i < numberOfBoxesToCheck; i++) {
                var txbID = $('.cssRptrTB').eq(i).attr('ID');
                ids[ids.length] = txbID;
            }
            return ids;
        }

</script>
    <script type="text/javascript">
        rapidSpell.setParameterValue("default", "ConsiderationRange", 500);

        function Ondialog_startedcheckingtextbox(src, textBox) {
            // debugger;
            //highlight current text box
            textBox.style.border = "2px solid blue";
        }

        function Ondialog_finishedcheckingtextbox(src, textBox) {
            //remove highlights on all text boxes
            for (var i = 0; i < rsw_rapidSpellControls.length; i++) {
                textBox = document.getElementById(rsTCInt[rsw_rapidSpellControls[i]].tbName);
                textBox.style.borderWidth = '';
                textBox.style.borderStyle = '';
                textBox.style.borderColor = '';
            }
        }


        function NotifyCorrection(src, tb, wordStart, oldWord, newWord) {
            var n = tb.indexOf("txt");
            var res = tb.substring(0, n + 3);
            var hfName = res + "_flagChange"
           
}


rapidSpell.addEventListener('dialog_startedcheckingtextbox', Ondialog_startedcheckingtextbox);
rapidSpell.addEventListener('dialog_finishedcheckingtextbox', Ondialog_finishedcheckingtextbox);
rapidSpell.addEventListener('dialog_correction', NotifyCorrection);


</script>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:Repeater ID="rptrLevels" runat="server">
        <ItemTemplate>
        <tr style="vertical-align: top" id="rptrDataRow">
        <td>
        <asp:TextBox ID="txtEntry" runat="server" Font-Size="11px" Height="60px"
        Text='<%# DataBinder.Eval(Container.DataItem, "FavoriteFoods")%>' TextMode="MultiLine" ToolTip="255 characters limit"
        Width="140px" CssClass="cssRptrTB"></asp:TextBox>
        </td>


        <td>
        <asp:TextBox ID="txtTarget" runat="server" Font-Size="11px" Height="60px"
        Text='<%# DataBinder.Eval(Container.DataItem, "FavoriteFoods")%>' TextMode="MultiLine" ToolTip="255 characters limit"
        Width="140px" CssClass="cssRptrTB"></asp:TextBox>
        </td>
        </tr>
        </ItemTemplate>
        </asp:Repeater>
   
    </div>


       
<input type="button" id='dialogBT' onclick="els = getContainedTextFields(); rapidSpell.dialog_spellCheck(true, els)"
value="Spell check" style="width: 90px; font-size: 12px" class="Button" />


    </form>
</body>
</html>




WebForm1.aspx.cs

Code:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace RSWEB_JS.SupportIssues.RepeaterDialog
{
    public partial class WebForm1 : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
            {
                ArrayList values = new ArrayList();

                values.Add(new PositionData("John", "Spagheti, Buffalo Wings"));
                values.Add(new PositionData("Jane", "Ice-cream, Staek"));
                values.Add(new PositionData("Harry", "Curry"));

                rptrLevels.DataSource = values;
                rptrLevels.DataBind();


            }
        }
    }

    public class PositionData
    {

        string m_name;
        string m_favoriteFoods;

        public PositionData(string name, String favoriteFoods)
        {
            m_name = name;
            m_favoriteFoods = favoriteFoods;
        }

        public string Name
        {
            get { return m_name; }
        }

        public string FavoriteFoods
        {
            get { return m_favoriteFoods; }
        }

    }
}



Jim



-your feedback is helpful to other users, thank you!


drudlin
#18 Posted : Wednesday, October 28, 2015 5:34:33 PM
Rank: Member

Groups: Registered

Joined: 7/7/2009
Posts: 12
Many thanks Jim. However I have already tried exactly that earlier today in your demo Repeater_Automatic_RSWL. It worked fine. We did it exactly as you have done and which is exactly as we have it in our own project.

Could there be a conflict with the other Keyoti spellcheckers throughout our project which have all been implemented with the Server Control method and which continue to work fine? Or could it be that we haven't yet set the URL to the userdictionary folder?

As I say, with this new JavaScript implementation, the dialog window opens but none of the controls are loaded into it so we are just seeing a blank modaldialog popup.
Dave
Jim
#19 Posted : Wednesday, October 28, 2015 7:11:46 PM
Rank: Advanced Member

Groups: Administrators, Registered

Joined: 8/13/2004
Posts: 2,667
Location: Canada
I should have said earlier - check for JS errors in the F12 console, I expect that'll shed some light....
-your feedback is helpful to other users, thank you!


drudlin
#20 Posted : Wednesday, October 28, 2015 10:24:18 PM
Rank: Member

Groups: Registered

Joined: 7/7/2009
Posts: 12
Thanks Jim. Nothing of significance in F12 console. I've checked and double-checked between the amended demo Repeater_Automatic_RSWL which works fine and our own project and can see no difference. I'm really at a loss!
Dave
2 Pages 12>
Forum Jump  
You cannot post new topics in this forum.
You cannot reply to topics in this forum.
You cannot delete your posts in this forum.
You cannot edit your posts in this forum.
You cannot create polls in this forum.
You cannot vote in polls in this forum.




About | Contact | Site Map | Privacy Policy

Copyright © 2002- Keyoti Inc.