Here are the before and after examples:
This simple Portlets example comes from the jQuery UI demo found at http://jqueryui.com/demos/sortable/#portlets.
The JavaScript Version:
(function (portlets, undefined) { portlets.init = function() { $(".column").sortable({ connectWith: ".column" }); $(".portlet") .addClass("ui-widget ui-widget-content ui-helper-clearfix ui-corner-all") .find(".portlet-header").addClass("ui-widget-header ui-corner-all") .prepend("<span class='ui-icon ui-icon-minusthick'></span>") .end().find(".portlet-content"); $(".portlet-header .ui-icon").click(function () { $(this).toggleClass("ui-icon-minusthick").toggleClass("ui-icon-plusthick"); $(this).parents(".portlet:first").find(".portlet-content").toggle(); }); $(".column").disableSelection(); }; } (window.portlets = window.portlets || {}));
The CoffeeScript Version:
((portlets) -> portlets.init = -> $(".column").sortable(connectWith: ".column").disableSelection() $(".portlet") .addClass("ui-widget ui-widget-content ui-helper-clearfix ui-corner-all") .find(".portlet-header").addClass("ui-widget-header ui-corner-all") .prepend("<span class='ui-icon ui-icon-minusthick'></span>") .end().find ".portlet-content" $(".portlet-header .ui-icon").click -> $(this).toggleClass("ui-icon-minusthick").toggleClass "ui-icon-plusthick" $(this).parents(".portlet:first").find(".portlet-content").toggle() ) window.portlets = window.portlets or {}
This simple Photo Manager example comes from the jQuery UI demo found at http://jqueryui.com/demos/droppable/#photo-manager.
The JavaScript Version:
(function (pictureManager, undefined) { pictureManager.init = function() { var $gallery = $("#gallery"), $trash = $("#trash"); $( "li", $gallery ).draggable({ cancel: "a.ui-icon", revert: "invalid", containment: $( "#demo-frame" ).length ? "#demo-frame" : "document", helper: "clone", cursor: "move" }); $trash.droppable({ accept: "#gallery > li", activeClass: "ui-state-highlight", drop: function( event, ui ) { deleteImage( ui.draggable ); } }); $gallery.droppable({ accept: "#trash li", activeClass: "custom-state-active", drop: function( event, ui ) { recycleImage( ui.draggable ); } }); $("ul.gallery > li").click(function (event) { var $item = $(this), $target = $(event.target); if ($target.is("a.ui-icon-trash")) { deleteImage($item); } else if ($target.is("a.ui-icon-zoomin")) { viewLargerImage($target); } else if ($target.is("a.ui-icon-refresh")) { recycleImage($item); } return false; }); var recycle_icon = "<a href='link/to/recycle/script/when/we/have/js/off' title='Recycle this image' class='ui-icon ui-icon-refresh'>Recycle image</a>"; function deleteImage($item) { $item.fadeOut(function () { var $list = $("ul", $trash).length ? $("ul", $trash) : $("<ul class='gallery ui-helper-reset'/>").appendTo($trash); $item.find("a.ui-icon-trash").remove(); $item.append(recycle_icon).appendTo($list).fadeIn(function () { $item .animate({ width: "48px" }) .find("img") .animate({ height: "36px" }); }); }); } var trash_icon = "<a href='link/to/trash/script/when/we/have/js/off' title='Delete this image' class='ui-icon ui-icon-trash'>Delete image</a>"; function recycleImage($item) { $item.fadeOut(function () { $item .find("a.ui-icon-refresh") .remove() .end() .css("width", "96px") .append(trash_icon) .find("img") .css("height", "72px") .end() .appendTo($gallery) .fadeIn(); }); } function viewLargerImage($link) { var src = $link.attr("href"), title = $link.siblings("img").attr("alt"), $modal = $("img[src$='" + src + "']"); if ($modal.length) { $modal.dialog("open"); } else { var img = $("<img alt='" + title + "' width='384' height='288' style='display: none; padding: 8px;' />") .attr("src", src).appendTo("body"); setTimeout(function () { img.dialog({ title: title, width: 400, modal: true }); }, 1); } } }; } (window.pictureManager = window.pictureManager || {}));
The CoffeeScript Version:
((pictureManager) -> pictureManager.init = -> $gallery = $("#gallery") $trash = $("#trash") recycle_icon = "<a href='link/to/recycle/script/when/we/have/js/off' title='Recycle this image' class='ui-icon ui-icon-refresh'>Recycle image</a>" trash_icon = "<a href='link/to/trash/script/when/we/have/js/off' title='Delete this image' class='ui-icon ui-icon-trash'>Delete image</a>" deleteImage = ($item) -> $item.fadeOut -> $list = if $("ul", $trash).length then $("ul", $trash) else $("<ul class='gallery ui-helper-reset'/>").appendTo $trash $item.find("a.ui-icon-trash").remove() $item.append(recycle_icon).appendTo($list).fadeIn -> $item.animate(width: "48px").find("img").animate height: "36px" recycleImage = ($item) -> $item.fadeOut -> $item.find("a.ui-icon-refresh").remove().end() .css("width", "96px").append(trash_icon).find("img") .css("height", "72px").end().appendTo($gallery).fadeIn() viewLargerImage = ($link) -> src = $link.attr "href" title = $link.siblings("img").attr "alt" $modal = $("img[src$='#{src}']") if $modal.length $modal.dialog "open" else img = $("<img alt='#{title}' width='384' height='288' style='display: none; padding: 8px;' />") .attr("src", src).appendTo "body" setTimeout (-> img.dialog title: title width: 400 modal: true ), 1 $("li", $gallery).draggable cancel: "a.ui-icon" revert: "invalid" containment: if $("#demo-frame").length then "#demo-frame" else "document" helper: "clone" cursor: "move" $trash.droppable accept: "#gallery > li" activeClass: "ui-state-highlight" drop: (event, ui) -> deleteImage ui.draggable $gallery.droppable accept: "#trash li" activeClass: "custom-state-active" drop: (event, ui) -> recycleImage ui.draggable $("ul.gallery > li").click (event) -> $item = $(this) $target = $(event.target) if $target.is "a.ui-icon-trash" deleteImage $item else if $target.is "a.ui-icon-zoomin" viewLargerImage $target else recycleImage $item if $target.is "a.ui-icon-refresh" false ) window.pictureManager = window.pictureManager or {}
This simple User Manager example comes from the jQuery UI demo found at http://jqueryui.com/demos/dialog/#modal-form.
The JavaScript Version:
(function (userManager, undefined) { userManager.init = function () { $("#dialog:ui-dialog").dialog("destroy"); var name = $("#name"), email = $("#email"), password = $("#password"), allFields = $([]).add(name).add(email).add(password), tips = $(".validateTips"); function updateTips(t) { tips .text(t) .addClass("ui-state-highlight"); setTimeout(function () { tips.removeClass("ui-state-highlight", 1500); }, 500); } function checkLength(o, n, min, max) { if (o.val().length > max || o.val().length < min) { o.addClass("ui-state-error"); updateTips("Length of " + n + " must be between " + min + " and " + max + "."); return false; } else { return true; } } function checkRegexp(o, regexp, n) { if (!(regexp.test(o.val()))) { o.addClass("ui-state-error"); updateTips(n); return false; } else { return true; } } $("#dialog-form").dialog({ autoOpen: false, height: 300, width: 350, modal: true, buttons: { "Create an account": function () { var bValid = true; allFields.removeClass("ui-state-error"); bValid = bValid && checkLength(name, "username", 3, 16); bValid = bValid && checkLength(email, "email", 6, 80); bValid = bValid && checkLength(password, "password", 5, 16); bValid = bValid && checkRegexp(name, /^[a-z]([0-9a-z_])+$/i, "Username may consist of a-z, 0-9, underscores, begin with a letter."); bValid = bValid && checkRegexp(email, /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i, "eg. ui@jquery.com"); bValid = bValid && checkRegexp(password, /^([0-9a-zA-Z])+$/, "Password field only allow : a-z 0-9"); if (bValid) { $("#users tbody").append("<tr>" + "<td>" + name.val() + "</td>" + "<td>" + email.val() + "</td>" + "<td>" + password.val() + "</td>" + "</tr>"); $(this).dialog("close"); } }, Cancel: function () { $(this).dialog("close"); } }, close: function () { allFields.val("").removeClass("ui-state-error"); } }); $("#create-user") .button() .click(function () { $("#dialog-form").dialog("open"); }); }; } (window.userManager = window.userManager || {}));
The CoffeeScript Version:
((userManager) -> userManager.init = -> name = $("#name") email = $("#email") password = $("#password") allFields = $([]).add(name).add(email).add password tips = $(".validateTips") updateTips = (t) -> tips.text(t).addClass "ui-state-highlight" setTimeout (-> tips.removeClass "ui-state-highlight", 1500 ), 500 checkLength = (o, n, min, max) -> if o.val().length > max or o.val().length < min o.addClass "ui-state-error" updateTips "Length of #{n} must be between #{min} and #{max}." false else true checkRegexp = (o, regexp, n) -> unless regexp.test o.val() o.addClass "ui-state-error" updateTips n false else true $("#dialog-form").dialog autoOpen: false height: 300 width: 350 modal: true buttons: "Create an account": -> bValid = true allFields.removeClass "ui-state-error" bValid = bValid and checkLength name, "username", 3, 16 bValid = bValid and checkLength email, "email", 6, 80 bValid = bValid and checkLength password, "password", 5, 16 bValid = bValid and checkRegexp name, /^[a-z]([0-9a-z_])+$/i, "Username may consist of a-z, 0-9, underscores, begin with a letter." bValid = bValid and checkRegexp email, /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i, "eg. ui@jquery.com" bValid = bValid and checkRegexp password, /^([0-9a-zA-Z])+$/, "Password field only allow : a-z 0-9" if bValid $("#users tbody").append "<tr>" + "<td>" + name.val() + "</td>" + "<td>" + email.val() + "</td>" + "<td>" + password.val() + "</td>" + "</tr>" $(this).dialog "close" Cancel: -> $(this).dialog "close" close: -> allFields.val("").removeClass "ui-state-error" $("#dialog:ui-dialog").dialog "destroy" $("#create-user").button().click -> $("#dialog-form").dialog "open" ) window.userManager = window.userManager or {}
I like the way you bind coffee here, something new for me, sadly there is no benefits from using F# in this example. Even I don't think that using F# as MVC Controller is good idea. I think it's better to use F# for some "Services" which controllers will call.
ReplyDeleteThanks for your comment. I agree that using F# for services, which the controllers would call, would be a good use for F#. However, I think there can still be benefits to using F# as the controller. While simple examples rarely display the available power of a language (especially when that isn't the focus of the example), it is clear that even this F# version is more succinct (and arguably more readable) than the equivalent C# version (which is also one of the benefits that I think CoffeeScript provides). Additionally, I would argue that the functional nature both of F# and JavaScript allow them to be a better fit from a paradigmatic perspective. That said, I agree that there are times when C# is a better tool for the job.
ReplyDeleteF#
ReplyDeleteHey
ReplyDeletethat's really a great post and a wonderful description out here, I really
like the way things are being executed and discussed here.
Cool! But could be more fun if you put these examples to work right here, in this post. Blogspot templates allow using jQuery and CoffeeScript on-the-fly compilation in posts, check out my set of tutorials at coffeequery.blogspot.com
ReplyDeleteI am sure this piece of writing has touched all the internet visitors, its really really nice piece of writing on building up new web site.
ReplyDeleteDo you have a spam issue on this blog; I also am a blogger, and I was wanting to know your situation; we have created some nice methods and we are looking to exchange methods with others, be sure to shoot me an e-mail if interested.
ReplyDeleteechpenPdopa Cody Bou https://wakelet.com/wake/TN0IHSjQqTfI-uIRgOkSf
ReplyDeletesteatlomolin
Welcihae-bo_Cincinnati Tracie Jackson Crack
ReplyDeletewheelscastinghar
stindinapht_n-Murfreesboro Gregg Gant download
ReplyDeletelink
download
https://colab.research.google.com/drive/19QuYfJ7B8SeSWIOobrcZYwoL38kTtoRl
gurgrabelmai
OcratasYtribre_Omaha Renee Davis VMware Workstation
ReplyDeleteExpress VPN
Avast Cleanup
recasedovs