@@ -1,11 +1,8 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Web.Mvc;
using Newtonsoft.Json;

@@ -21,12 +18,12 @@ public static class HtmlCustom
public static MvcHtmlString MyDropDownListFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression)
{
var propertyName = ExpressionHelper.GetExpressionText(expression);
var dropDownvalues = JsonConvert.SerializeObject(GetDropDownValues(expression).Select(d => new { Key = d.Key, Text = d.Value }),new JsonSerializerSettings{});
var dropDownvalues = JsonConvert.SerializeObject(GetDropDownValues(expression).Select(d => new { Key = d.Key, Text = d.Value }));
return new MvcHtmlString(string.Format(
"<select class='form-control' data-bind='options: {0}, optionsText: \"{1}\", optionsValue:\"{2}\", value: {3}, optionsCaption: \"{4}\"'></select>",
dropDownvalues,
"Text", "Key",
propertyName.ToCamelCase(), //NOTE this should not be a quoted value
dropDownvalues,
"Text", "Key",
propertyName.ToCamelCase(), //NOTE this should NOT be a quoted value
"Choose..."));
}
public static MvcHtmlString MyTextInputFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression)
@@ -35,11 +32,11 @@ public static class HtmlCustom
}
public static MvcHtmlString MyTelInputFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression)
{
return GetInput("text", ExpressionHelper.GetExpressionText(expression));
return GetInput("tel", ExpressionHelper.GetExpressionText(expression));
}
public static MvcHtmlString MyEmailInputFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression)
{
return GetInput("text", ExpressionHelper.GetExpressionText(expression));
return GetInput("email", ExpressionHelper.GetExpressionText(expression));
}
public static MvcHtmlString MyCheckboxInputFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression)
{
@@ -85,18 +82,14 @@ private static MvcHtmlString GetInput(string inputType, string propertyName, par
private static bool IsDefaultEnum(object enumValue)
{
var type = enumValue.GetType();
var val = type.GetField(enumValue.ToString());
return (enumValue.Equals(GetDefault(type))) &&
enumValue.ToString().Equals("None", StringComparison.InvariantCultureIgnoreCase);
enumValue.ToString().Equals("None", StringComparison.InvariantCultureIgnoreCase);
}
public static object GetDefault(Type type)
{
if (type.IsValueType)
{
return Activator.CreateInstance(type);
}
return null;
return type.IsValueType ? Activator.CreateInstance(type) : null;
}

private static string GetEnumDescription(object enumValue)
{
return enumValue.GetType()
@@ -107,60 +100,4 @@ private static string GetEnumDescription(object enumValue)
.SingleOrDefault() ?? enumValue.ToString();
}
}

public static class PropertyInfoExtensions
{
public static bool AttributeExists<T>(this PropertyInfo propertyInfo) where T : class
{
var attribute = propertyInfo.GetCustomAttributes(typeof(T), false)
.FirstOrDefault() as T;
if (attribute == null)
{
return false;
}
return true;
}

public static bool AttributeExists<T>(this Type type) where T : class
{
var attribute = type.GetCustomAttributes(typeof(T), false).FirstOrDefault() as T;
if (attribute == null)
{
return false;
}
return true;
}

public static T GetAttribute<T>(this Type type) where T : class
{
return type.GetCustomAttributes(typeof(T), false).FirstOrDefault() as T;
}

public static T GetAttribute<T>(this PropertyInfo propertyInfo) where T : class
{
return propertyInfo.GetCustomAttributes(typeof(T), false).FirstOrDefault() as T;
}

public static string LabelFromType(Type @type)
{
var att = GetAttribute<DisplayNameAttribute>(@type);
return att != null ? att.DisplayName
: @type.Name.ToSeparatedWords();
}

public static string GetLabel(this Object model)
{
return LabelFromType(model.GetType());
}

public static string GetLabel(this IEnumerable model)
{
var elementType = model.GetType().GetElementType();
if (elementType == null)
{
elementType = model.GetType().GetGenericArguments()[0];
}
return LabelFromType(elementType);
}
}
}
@@ -0,0 +1,23 @@
using System.Text.RegularExpressions;

namespace ImageUploader.Web.Extensions
{
public static class StringExtensions
{
public static string ToSeparatedWords(this string value)
{
return Regex.Replace(value, "([A-Z][a-z])", " $1").Trim();
}

public static string ToCamelCase(this string value)
{
// If there are 0 or 1 characters, just return the string.
if (value == null || value.Length < 2)
return value;

// Split the string into words.
value = value.Trim().Replace(" ", "_");
return value.Substring(0, 1).ToLower() + value.Substring(1);
}
}
}
@@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
@@ -136,10 +136,8 @@
<Compile Include="App_Start\RouteConfig.cs" />
<Compile Include="App_Start\WebApiConfig.cs" />
<Compile Include="Controllers\HomeController.cs" />
<Compile Include="Controllers\ImagesController.cs" />
<Compile Include="Controllers\ValuationRequestController.cs" />
<Compile Include="Extensions\HtmlHelperExtensions.cs" />
<Compile Include="Extensions\DefaultScaffoldingExtensions.cs" />
<Compile Include="Extensions\StringExtensions.cs" />
<Compile Include="Extensions\PropertyInfoExtensions.cs" />
<Compile Include="Global.asax.cs">
<DependentUpon>Global.asax</DependentUpon>
@@ -157,15 +155,8 @@
<Content Include="Scripts\bootstrap.js" />
<Content Include="Scripts\bootstrap.min.js" />
<None Include="Scripts\jquery-1.10.2.intellisense.js" />
<Content Include="Scripts\Inhouse\FormUtilities.js" />
<Content Include="Scripts\Inhouse\ImageUpload.js" />
<Content Include="Scripts\jquery-1.10.2.js" />
<Content Include="Scripts\jquery-1.10.2.min.js" />
<None Include="Scripts\jquery.validate-vsdoc.js" />
<Content Include="Scripts\jquery.validate.js" />
<Content Include="Scripts\jquery.validate.min.js" />
<Content Include="Scripts\jquery.validate.unobtrusive.js" />
<Content Include="Scripts\jquery.validate.unobtrusive.min.js" />
<Content Include="Scripts\knockout-2.3.0.debug.js" />
<Content Include="Scripts\knockout-2.3.0.js" />
<Content Include="Scripts\knockout.validation.js" />
@@ -187,7 +178,6 @@
<Content Include="Views\Shared\_Layout.cshtml" />
<Content Include="Views\Home\Index.cshtml" />
<Content Include="Scripts\jquery-1.10.2.min.map" />
<Content Include="Views\Home\Valuation.cshtml" />
<Content Include="Scripts\knockout.validation.min.js.map" />
</ItemGroup>
<ItemGroup>

This file was deleted.

This file was deleted.

Binary file not shown.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

@@ -1,23 +1,33 @@
@{
ViewBag.Title = "Home Page";
@using ImageUploader.Web.Extensions
@model ImageUploader.Web.Models.ValuationRequest

@{
ViewBag.Title = "Valuation";
}

<style>
#holder {
border: 10px dashed #ccc;
width: 300px;
min-height: 300px;
margin: 20px auto;
.validationMessage {
color: red;
}
#holder {
border: 1px solid #ddd;
border-radius: 4px 4px 0 0;
/*width: 100%;*/
/*min-height: 140px;*/
margin: 20px auto;
padding: 20px;
text-align: center;
vertical-align: middle;
}
#holder.hover {
border: 10px dashed #0c0;
}
#holder img {
display: block;
margin: 10px auto;
}
/*#holder img {
display: block;
margin: 10px auto;
}*/
#holder p {
margin: 10px;
@@ -41,19 +51,327 @@
.hidden {
display: none !important;
}
/*http://stackoverflow.com/questions/11235206/twitter-bootstrap-form-file-element-upload-button*/
.btn-file {
position: relative;
overflow: hidden;
}
.btn-file input[type=file] {
position: absolute;
top: 0;
right: 0;
min-width: 100%;
min-height: 100%;
font-size: 100px;
text-align: right;
filter: alpha(opacity=0);
opacity: 0;
outline: none;
background: white;
cursor: inherit;
display: block;
}
</style>

<article>
<div id="holder">
<h2>Online Valuation</h2>

@using (Html.BeginForm("", "ValuationRequest", FormMethod.Post, new { id = "requestValuationForm", @class = "form-horizontal", role = "form", enctype = "multipart/form-data" }))
{
@Html.AntiForgeryToken()

<div class="form-horizontal">
<hr />
@Html.ValidationSummary(true, "", new { @class = "text-danger" })

<fieldset class="form-horizontal">
<legend>Item Details</legend>
<div class="form-group">
@Html.MyLabelFor(model => model.DescriptionOfItemToSell)
<div class="col-md-10">
@Html.MyTextInputFor(model => model.DescriptionOfItemToSell)
</div>
</div>

<div class="form-group">
@Html.MyLabelFor(model => model.MakeOrManufacturer)
<div class="col-md-10">
@Html.MyTextInputFor(model => model.MakeOrManufacturer)
</div>
</div>

<div class="form-group">
@Html.MyLabelFor(model => model.Model)
<div class="col-md-10">
@Html.MyTextInputFor(model => model.Model)
</div>
</div>

<div class="form-group">
@Html.MyLabelFor(model => model.SerialNumber)
<div class="col-md-10">
@Html.MyTextInputFor(model => model.SerialNumber)
</div>
</div>

<div class="form-group">
@Html.MyLabelFor(model => model.OtherDetails)
<div class="col-md-10">
@Html.MyTextInputFor(model => model.OtherDetails)
</div>
</div>

<div class="form-group">
@Html.MyLabelFor(model => model.Condition)
<div class="col-md-10">
@Html.MyDropDownListFor(model => model.Condition)
</div>
</div>

<div class="form-group">
@Html.MyLabelFor(model => model.AmountRequestedForProperty)
<div class="col-md-10">
@Html.MyTextInputFor(model => model.AmountRequestedForProperty)
</div>
</div>


<div class="form-group">
@Html.MyLabelFor(model => model.Images)
<div class="col-md-10">
<span class="btn btn-primary btn-file">
Upload images <input type="file" name="images" multiple accept="image/*"
data-bind="event: { change: function() { $root.setFiles($element.files) } }"
class="btn btn-default" />
</span>
<p class="validationMessage" data-bind="validationMessage: images"></p>
<div id="holder">
Pending image upload&hellip;
</div>
</div>
</div>

</fieldset>

<fieldset class="form-horizontal">
<legend>Customer Details</legend>

<div class="form-group">
@Html.MyLabelFor(model => model.Name)
<div class="col-md-10">
@Html.MyTextInputFor(model => model.Name)
</div>
</div>

<div class="form-group">
@Html.MyLabelFor(model => model.IsCustomerOver18YearsOld)
<div class="col-md-10">
<div class="checkbox">
@Html.MyCheckboxInputFor(model => model.IsCustomerOver18YearsOld)
</div>
</div>
</div>
</fieldset>

<div class="form-group">
@Html.MyLabelFor(model => model.Email)
<div class="col-md-10">
@Html.MyEmailInputFor(model => model.Email)
</div>
</div>

<div class="form-group">
@Html.MyLabelFor(model => model.Phone)
<div class="col-md-10">
@Html.MyTelInputFor(model => model.Phone)
</div>
</div>

<div class="form-group">
@Html.MyLabelFor(model => model.IsCustomerRighfulOwner)
<div class="col-md-10">
<div class="checkbox">
@Html.MyCheckboxInputFor(model => model.IsCustomerRighfulOwner)
</div>
</div>
</div>

<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
<p id="upload" class="hidden"><label>Drag & drop not supported, but you can still upload via this input field:<br><input type="file"></label></p>
<p id="filereader">File API & FileReader API not supported</p>
<p id="formdata">XHR2's FormData is not supported</p>
<p id="progress">XHR2's upload progress isn't supported</p>
<p>Upload progress: <progress id="uploadprogress" min="0" max="100" value="0">0</progress></p>
<p>Drag an image from your desktop on to the drop zone above to see the browser both render the preview, but also upload automatically to this server.</p>
</article>
}

<div>
@Html.ActionLink("Back to List", "Index")
</div>

@section scripts {
<script src="~/Scripts/Inhouse/ImageUpload.js"></script>
}

<script>
//HTML features we may want to use
var tests = {
filereader: typeof FileReader != 'undefined',
dnd: 'draggable' in document.createElement('span'),
formdata: !!window.FormData,
progress: "upload" in new XMLHttpRequest
};
//TODO redirect if these test fail
ko.validation.rules['phoneAUS'] = {
validator: function (phoneNumber, validate) {
if (typeof (phoneNumber) !== 'string') {
return false;
}
if (ko.validation.utils.isEmptyVal(phoneNumber)) {
return true;
} // makes it optional, use 'required' rule if it should be required
phoneNumber = phoneNumber.replace(/\s+/g, "");
return validate && phoneNumber.length === 10 &&
phoneNumber.match(/^(0(2|3|4|7|8))?\d{8}$/);
},
message: 'Please specify a valid australian phone number'
};
ko.validation.rules["mustBeTrue"] = {
validator: function (val, validate) {
if (!validate) {
return true;
}
return val === true;
}
};
ko.validation.registerExtenders();
var ValuationRequest = function () {
var self = this;
self.name = ko.observable().extend({ required: true });
self.isCustomerOver18YearsOld = ko.observable().extend({ required: true, mustBeTrue: { message: 'Customer must be over 18' } });
self.descriptionOfItemToSell = ko.observable().extend({ required: true });
self.makeOrManufacturer = ko.observable();
self.model = ko.observable();
self.serialNumber = ko.observable();
self.condition = ko.observable().extend({ required: true, minLength: 1 }); //enum just dont select the empty option
self.otherDetails = ko.observable();
self.images = ko.observableArray().extend({ required: true });
self.isCustomerRighfulOwner = ko.observable().extend({ required: true, mustBeTrue: { message: 'Customer must be the rightful owner' } });
self.amountRequestedForProperty = ko.observable().extend({ required: true , number:true});
self.email = ko.observable().extend({ required: true, email: true, });
self.phone = ko.observable().extend({ required: true, phoneAUS: true });
var errors = ko.validation.group(self, { deep: true, observable: true });
self.canSubmit = ko.computed(function () {
return errors().length === 0;
});
self.showAllMessages = function () {
errors.showAllMessages();
};
self.setFiles = function (files) {
$("#holder").empty();
self.images.removeAll();
console.log(files);
for (var i = 0; i < files.length; i++) {
self.images.push(files[i]);
previewfile(files[i], $("#holder"));
}
};
}
function previewfile(file, $element) {
console.log("previewfile");
console.log(file);
var img = document.createElement("img");
img.src = window.URL.createObjectURL(file);
img.className = "img-responsive img-thumbnail";
img.onload = function () {
window.URL.revokeObjectURL(this.src);
}
$element.append(img);
}
vm = new ValuationRequest();
ko.applyBindings(vm);
function postAjax() {
if (!vm.canSubmit()) {
vm.showAllMessages();
return;
}
var formData = new FormData();
formData.append('Name', vm.name());
formData.append('IsCustomerOver18YearsOld', vm.isCustomerOver18YearsOld());
formData.append('DescriptionOfItemToSell', vm.descriptionOfItemToSell());
formData.append('MakeOrManufacturer', vm.makeOrManufacturer());
formData.append('Model', vm.model());
formData.append('SerialNumber', vm.serialNumber());
formData.append('Condition', vm.condition());
formData.append('OtherDetails', vm.otherDetails());
formData.append('IsCustomerRighfulOwner', vm.isCustomerRighfulOwner());
formData.append('AmountRequestedForProperty', vm.amountRequestedForProperty());
formData.append('Email', vm.email());
formData.append('Phone', vm.phone());
for (var i = 0; i < vm.images().length; i++) {
var image = vm.images()[i];
console.log(image);
formData.append('Images[' + i + ']', image);//Conforms to the MultipartDataMediaFormatter expecations
}
var xhr = new XMLHttpRequest();
xhr.open('POST', '/api/ValuationRequest');
//xhr.onload = function () {
// progress.value = progress.innerHTML = 100;
//};
if (tests.progress) {//TODO REMOVE - we should just bail if their browser sucks
console.log("Can show progress");
xhr.upload.onprogress = function (event) {
console.log("making progress");
if (event.lengthComputable) {
console.log("event length is Computable");
var complete = (event.loaded / event.total * 100 | 0);
console.log("event.loaded / event.total = " + event.loaded + " / " + event.total);
// progress.value = progress.innerHTML = complete;
}
}
}
console.log(formData);
xhr.send(formData);
}
$('#requestValuationForm').submit(function (e) {
e.preventDefault();
console.log(ko.toJS(vm));
try {
postAjax();
} catch (e) {
console.log(e);
debugger;
throw e;
}
});
//Prefill the form - delete me if you want
vm.name("Rhys");
vm.isCustomerOver18YearsOld(true);
vm.descriptionOfItemToSell("Fender guitar");
vm.makeOrManufacturer("fender");
vm.model("Stratocaster");
vm.serialNumber("12311222");
//self.condition = ko.observable().extend({ required: true, minLength: 1 }); //enum just dont select the empty option
vm.otherDetails("Played by hendrix");
//self.images = ko.observableArray().extend({ required: true });
vm.isCustomerRighfulOwner(true);
vm.amountRequestedForProperty(750);
vm.email("asdasd@asdasd.asd");
vm.phone("0788877766");
//END Prefill the form
</script>
}

This file was deleted.

@@ -21,8 +21,8 @@
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li>@Html.ActionLink("Home", "Index", "Home")</li>
<li>@Html.ActionLink("Online Valuation", "Valuation", "Home")</li>
<li><a href="http://html5demos.com/dnd-upload">Original Source</a></li>
<li><a href="http://html5demos.com/dnd-upload">Original Client Source</a></li>
<li><a href="https://github.com/iLexDev/ASP.NET-WebApi-MultipartDataMediaFormatter">Multipart Media Formatter</a></li>
</ul>
</div>
</div>
@@ -3,7 +3,6 @@
<package id="Antlr" version="3.4.1.9004" targetFramework="net45" />
<package id="bootstrap" version="3.0.0" targetFramework="net45" />
<package id="jQuery" version="1.10.2" targetFramework="net45" />
<package id="jQuery.Validation" version="1.11.1" targetFramework="net45" />
<package id="Knockout.Validation" version="2.0.3" targetFramework="net45" />
<package id="knockoutjs" version="2.3.0" targetFramework="net45" />
<package id="Microsoft.AspNet.Mvc" version="5.2.3" targetFramework="net45" />
@@ -14,7 +13,6 @@
<package id="Microsoft.AspNet.WebApi.Core" version="5.2.3" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.WebHost" version="5.2.3" targetFramework="net45" />
<package id="Microsoft.AspNet.WebPages" version="3.2.3" targetFramework="net45" />
<package id="Microsoft.jQuery.Unobtrusive.Validation" version="3.2.3" targetFramework="net45" />
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net45" />
<package id="Modernizr" version="2.6.2" targetFramework="net45" />
<package id="Newtonsoft.Json" version="6.0.4" targetFramework="net45" />