autocapitalize="words" broken on mobile Safari in iOS 8,9 - Is there a workaround? - ios8

The <input> attribute autocapitalize="words" is broken in mobile Safari under iOS 8,9 with the default iOS keyboard. It uppercases the first 2 letters of the field, not the first letter of each word.
Official documentation says is supported: https://developer.apple.com/library/archive/documentation/AppleApplications/Reference/SafariHTMLRef/Articles/Attributes.html
To test, open the following field on iOS emulator or real device:
First name: <input type="text" autocorrect="off" autocapitalize="words" value="First Name">
You can use https://www.w3schools.com/tags/tryit.asp?filename=tryhtml_form_submit to test, or this snippet on iOS 8 or 9:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Test autocapitalize</title>
</head>
<body>
<form>
<label for="words">autocapitalize="words"</label>
<input type="text" autocapitalize="words" name="text1" id="words" /><br />
<label for="sentences">autocapitalize="sentences"</label>
<input type="text" autocapitalize="sentences" name="text2" id="sentences" /><br />
<label for="none">autocapitalize="none"</label>
<input type="text" autocapitalize="none" name="text3" id="none" />
</form>
</body>
</html>
I'm amazed this has been present since 8.x and has passed under the radar.
Is there a known workaround?
Update 10/13:
iPhone 6s+ Safari completely ignores any HTML attribute set on the input field.

There seems to be a workaround for this issue if you are willing to (temporarily) include this library: https://github.com/agrublev/autocapitalize. It does however require jQuery, so might not be ideal on a mobile device. I've created this small piece of code which does the same thing just for words without the use of jQuery. It can ofcourse be extented to also include other cases.
The example below also capitalizes the words initially on page ready, instead of just on the 'keyup' event. I've tested the code on several devices and haven't gotten an error. But feel free to comment if something doesn't work or you feel something can be done better.
Note that the 'domReady' function I added works for IE9 and up. Please see this if you require support for an older version.
// Create one global variable
var lib = {};
(function ( lib ) {
lib.autocapitalize_element = function (element) {
var val = element.value.toLowerCase();
var split_identifier = " ";
var split = val.split(split_identifier);
for (var i = 0; i < split.length; i ++) {
var v = split[i];
if ( v.length ) {
split[i] = v.charAt(0).toUpperCase() + v.substring(1);
}
};
val = split.join(split_identifier);
element.value = val;
}
lib.autocapitalize_helper = function(element) {
element.onkeyup = function(e) {
var inp = String.fromCharCode(e.keyCode);
if (/[a-zA-Z0-9-_ ]/.test(inp)) {
lib.autocapitalize_element(element);
}
};
}
lib.autocapitalize = function() {
var elements = document.querySelectorAll("input[autocapitalize], textarea[autocapitalize]");
for(var i = 0; i < elements.length; i++) {
lib.autocapitalize_helper(elements[i]);
lib.autocapitalize_element(elements[i]);
}
}
lib.domReady = function(callback) {
document.readyState === "interactive" || document.readyState === "complete" ? callback() : document.addEventListener("DOMContentLoaded", callback);
};
}( lib ));
// This function gets called when the dom is ready. I've added it to the lib variable now because I dislike adding global variables, but you can put it anywhere you like.
lib.domReady(function() {
lib.autocapitalize();
});

Related

Uploading gallery images using filesystem in Entity Framework

I want to upload gallery images to ASP.NET MVC 5 application using filesystem upload. I added
public IEnumerable<string> GalleryImages { get; set; }
to my ProductModel, built solution and performed update-database in the package manager console. But, the property is not added to the Product table and when I try to add and then edit a product, I get this error:
System.ArgumentNullException: 'Value cannot be null.
Parameter name: source'
Also, I added this piece of code to Edit.cshtl:
#if (!Model.GalleryImages.Any())
{
<h4>There are no gallery images for this product.</h4>
}
<form action="/AdminPanel/Product/SaveGalleryImages" method="post" enctype="multipart/form-data" class="dropzone" id="dropzoneForm">
<div class="fallback">
<input type="file" name="file" multiple />
<input type="submit" value="Upload" />
</div>
</form>
<br /><br />
#foreach (var image in Model.GalleryImages)
{
<div style="display: inline-block">
<img src="/Images/Uploads/Products/#Model.Id/Gallery/Thumbs/#image" />
#Html.ActionLink("delete", "DeleteImage", "Product", new { #class = "deleteimage", data_name = image })
</div>
}
<link href="~/Scripts/dropzone/basic.css" rel="stylesheet" />
<link href="~/Scripts/dropzone/dropzone.css" rel="stylesheet" />
#section Scripts{
<script src="~/Scripts/dropzone/dropzone.js"></script>
<script>
$(function () {
//preview image
function readURL(input) {
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function (e) {
$("img#imgpreview").attr("src", e.target.result).width(200).height(200);
}
reader.readAsDataURL(input.files[0]);
}
}
$("#ImageUpload").change(function () {
readURL(this);
});
//dropzone js
Dropzone.options.dropzoneForm = {
acceptedFiles: "image/*",
init: function () {
this.on("complete", function (file) {
if (this.getUploadingFiles().length === 0 && this.getQueuedFiles().length === 0) {
location.reload();
}
});
this.on("sending", function (file, xhr, formData) {
formData.append("id", #Model.Id);
});
}
};
//dropzone js
$("a.deleteimage").click(function (e) {
e.preventDefault();
if (!confirm("confirm deletion"))
return false;
var $this = $(this);
var url = "/admin/shop/DeleteImage";
var imageName = $this.data("name");
$.post(url, { id: #Model.Id, imageName: imageName }, function (data) {
$this.parent().fadeOut("fast");
});
});
});
</script>
}
UPDATE:
What exactly do you expect as a column datatype when you model it as List<string>? SQL Server doesn't have any column data type to handle an arbitrary list of strings....
If you have an 1:n relationship between ProductModel and gallery images, you should really have a separate model class that holds the image information - e.g. MyImages. Then you could add a collection-style property
public virtual ICollection<MyImages> GalleryImages
to your ProductModel class.
SQL Server can't really handle List<string> as a column type .....
The steps needed to get this done are:
Change your C# model class (you've done this already)
Run add-migration migration-name so that an EF migration is added to your project (you seem to have skipped this step)
Run update-database to actually apply that migration to the database.
Only if you've completed ALL 3 steps in exactly this order are changes from your C# model class actually applied to the database - you CANNOT simply skip step #2 ....

Parameters not being properly passed to controller action from view

For an app I am working on, I've got the following Razor code for a View I am working on:
#Html.InputFor(m => m.Property1); // A date
#Html.InputFor(m => m.Property2); // Some other date
#Html.InputFor(m => m.SomeOtherProperty); // Something else.
<a href='#' id='some-button'>Button Text Here</a>
<!-- SNIP: Extra code that dosen't matter -->
<script>
var $someButton = $('#some-button');
$(document).ready(function () {
$someButton.click(function (e) {
e.preventDefault();
window.open('#Url.Action("Foo", "Home", new {p1 = Model.Property1, p2 = Model.Property2, pX = Model.SomeOtherProperty})', '_blank');
});
});
</script>
...upon a comment, I checked the rendered HTML. The values come with values, as expected...
<input name="Property1" data-val="true" data-val-required="(Required)" type="text" value="1/1/2013">
<input name="Property2" data-val="true" data-val-required="(Required)" type="text" value="4/11/2013">
<input name="SomeOtherProperty" data-val="true" data-val-required="(Required)" type="text" value="42">
<a href='#' id='some-button'>Button Text Here</a>
<script>
var $someButton = $('#some-button');
$(document).ready(function () {
$someButton.click(function (e) {
e.preventDefault();
window.open('http://localhost:xxxx/Home/Foo?p1=1%2F1%2F2013&p2=4%2F11%2F2013&pX=42', '_blank');
});
});
</script>
...and on the server side...
public ActionResult Foo(string p1, string p2, string pX)
{
var workModel = new FooWorkModel
{
Property1 = p1,
Property2 = p2,
SomeOtherProperty = pX
};
// Do something with this model, dosen't really matter from here, though.
return new FileContentResult(results, "application/some-mime-type");
}
I've noticed that only the first parameter (p1) is getting a value from the front end; all my other parameters are being passed null values!
Question: Why is the ActionResult being passed null values, when some value is assigned for these other fields? Or, a complimentary question: why would only the first parameter be successfully passing its value, while everything else is failing?
The issue is being caused by the escaped URL being generated by Url.Action(). (Source: How do I pass correct Url.Action to a JQuery method without extra ampersand trouble?)
Simply add a #Html.Raw() call around the Url.Action(), and data will flow as intended.
window.open('#Html.Raw(Url.Action("Foo", "Home", new {p1 = Model.Property1, p2 = Model.Property2, pX = Model.SomeOtherProperty}))', '_blank');

JQuery Load using MVC3 #Url.Action does not pass parameters properly

I noticed that doing #Url.Action("myAction", new { param1 = 123, param2 = 456}) provides me with an invalid URL Home/myAction?param1=123&param2=456.
I am attempting to do
$("#myAjaxDiv").load(url);
But only param1 is getting populated in the action method.
When I remove the & and make it just & then it works, but doing a string replace is super hacky.
url = url.replace("&", "&");
Am I missing something here?
EDIT: Per request I'm including some of my sample app. (you can create a new MVC app and just add these quickly and see for yourself)
Controller:
public ActionResult AjaxTest(int? year, int? month)
{
ViewBag.Message = string.Format("Year: {0}, Month: {1}", year.HasValue ? year.ToString() : "no year", month.HasValue ? month.ToString() : "no month");
return PartialView("AjaxTest");
}
AjaxTest View:
#ViewBag.Message
Index View:
<script>
$(function () {
var url="";
$("#noParams").click(function () {
url = "Home/AjaxTest";
$("#ajaxy").load(url)
$("#url").text(url);
});
$("#yearParam").click(function () {
url = "Home/AjaxTest?year=2012";
$("#ajaxy").load(url)
$("#url").text(url);
});
$("#yearAndMonthParam").click(function () {
url = "Home/AjaxTest?year=2012&month=10";
$("#ajaxy").load(url)
$("#url").text(url);
});
$("#generated").click(function () {
url = "#(Url.Action("AjaxTest", new { year=2012, month=10}))";
$("#ajaxy").load(url);
$("#url").text(url);
});
});
</script>
<a id="noParams" href="#">No Params</a> <br />
<a id="yearParam" href="#">Year Param</a> <br />
<a id="yearAndMonthParam" href="#">Year and Month Param</a> <br />
<a id="generated" href="#">Generated</a> <br />
<div id="ajaxy">
</div>
<div>
URL: <span id="url"></span>
</div>
By default every content (which is not IHtmlString) emitted using a # block is automatically HTML encoded by Razor (see this Razor intro article Html Encoding section)
The Url.Action returns just a plain string so thats why the & gets encoded.
Use the Html.Raw if you don't want the encodeing:
url = "#(Html.Raw(Url.Action("AjaxTest", new { year=2012, month=10})))";
You can build the url in this way also.
var url = "#Url.Action("AjaxTest","YourControllerName")?year=2012&month=10";
$("#ajaxy").load(url);

need to make sure my ajax script is set up right

ajax code:
try {
xmlhttp = new XMLHttpRequest();
}
catch(ee) {
try {
xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
}
catch(e) {
try {
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
catch(E) {
xmlhttp = false;
}
}
}
div_base = "";
valor = 0;
function abre(arquivo,metodo,div) {
div_base = div;
valor++;
xmlhttp.open(metodo,arquivo+"?valor="+valor);
xmlhttp.onreadystatechange=response
xmlhttp.send(null)
}
function response() {
nova_div = div_base;
document.getElementById(nova_div).innerHTML="<div>Loading...</div>"
if (xmlhttp.readyState==4) {
document.getElementById(nova_div).innerHTML=xmlhttp.responseText
}
}
html code:
<form>
<select name="menu" style="width:400px; height:25px;">
<option>Change Theme:</option>
<option></option>
<option onclick="javascript: abre('Chat_Themes/Default.html','GET','response2');">Default - Shadow Hunters</option>
<option onclick="javascript: abre('Chat_themes/Custom.html','GET','response2');">Custom - Shadow Hunters</option>
</select>
</form>
<br />
<div id="response2"></div>
i changed the "div = responce" to "div = responce2" without changing the ajax code at the top, im not sure if i have to change the ajax code or not or i can leave it and it works fine the way it is, but it does not work on google chrome idk if its just google chrome being retarded, but it works in ff and ie just fine, any thoughts?
Try indenting your code: you'll find that your try-catch statements don't have matching braces. You can also try a Javascript-validating service like jshint, but indenting should come first.
You might want to consider using a third-party library which already has cross-browser AJAX capability, like jQuery.

Remove CKEdit Instance

I can't seem to destroy instances of CKEdit per the documentation.
Consider the following:
<input name="txt1" type="text" id="txt1" /><br />
Create<br />
Destroy
<script type= "text/javascript" >
<!--
function create() {
var hEd = CKEDITOR.instances['txt1'];
if (hEd) {
CKEDITOR.remove(hEd);
}
hEd = CKEDITOR.replace('txt1');
}
function destroy(){
var hEd = CKEDITOR.instances['txt1'];
if (hEd) {
CKEDITOR.remove(hEd);
}
}
-->
</script>
When destroy() runs, CKEDITOR.remove(hEd); is being called. Multiple clicks to create() produce multiple instances of CKEditor on screen, but their instances no longer appear in CKEDITOR.instances.
Am I missing something?
You must use hEd.destroy (editor.destroy()).
CKEDITOR.remove() is for internal use as stated in the API.
Simple solution
CKEDITOR.instances['textareaId'].destory()
You must use:
<textarea name="tx1" id="tx1" rows="15" cols="106"></textarea>
CKEDITOR.instances['tx1'] = false;

Resources