/* * jQuery dform plugin * Copyright (C) 2012 David Luecke <daff@neyeon.com>, [http://daffl.github.com/jquery.dform] * * Licensed under the MIT license */ (function ($) { var _subscriptions = {}, _types = {}, each = $.each, addToObject = function (obj) { var result = function (data, fn, condition) { if (typeof data === 'object') { $.each(data, function (name, val) { result(name, val, condition); }); } else if (condition === undefined || condition === true) { if (!obj[data]) { obj[data] = []; } obj[data].push(fn); } } return result; }, isArray = $.isArray, /** * Returns an array of keys (properties) contained in the given object. * * @param {Object} object The object to use * @return {Array} An array containing all properties in the object */ keyset = function (object) { return $.map(object, function (val, key) { return key; }); }, /** * Returns an object that contains all values from the given * object that have a key which is also in the array keys. * * @param {Object} object The object to traverse * @param {Array} keys The keys the new object should contain * @return {Object} A new object containing only the properties * with names given in keys */ withKeys = function (object, keys) { var result = {}; each(keys, function (index, value) { if (object[value]) { result[value] = object[value]; } }); return result; }, /** * Returns an object that contains all value from the given * object that do not have a key which is also in the array keys. * * @param {Object} object The object to traverse * @param {Array} keys A list of keys that should not be contained in the new object * @return {Object} A new object with all properties of the given object, except * for the ones given in the list of keys */ withoutKeys = function (object, keys) { var result = {}; each(object, function (index, value) { if (!~$.inArray(index, keys)) { result[index] = value; } }); return result; }, /** * Run all subscriptions with the given name and options * on an element. * * @param {String} name The name of the subscriber function * @param {Object} options ptions for the function * @param {String} type The type of the current element as in the registered types * @return {Object} The jQuery object */ runSubscription = function (name, options, type) { if ($.dform.hasSubscription(name)) { this.each(function () { var element = $(this); each(_subscriptions[name], function (i, sfn) { // run subscriber function with options sfn.call(element, options, type); }); }); } return this; }, /** * Run all subscription functions with given options. * * @param {Object} options The options to use * @return {Object} The jQuery element this function has been called on */ runAll = function (options) { var type = options.type, self = this; // Run preprocessing subscribers this.dform('run', '[pre]', options, type); each(options, function (name, sopts) { self.dform('run', name, sopts, type); }); // Run post processing subscribers this.dform('run', '[post]', options, type); return this; }; /** * Globals added directly to the jQuery object */ $.extend($, { keyset : keyset, withKeys : withKeys, withoutKeys : withoutKeys, dform : { /** * Default options the plugin is initialized with: * * ## prefix * * The Default prefix used for element classnames generated by the dform plugin. * Defaults to _ui-dform-_ * E.g. an element with type text will have the class ui-dform-text * */ options : { prefix : "ui-dform-" }, /** * A function that is called, when no registered type has been found. * The default behaviour returns an HTML element with the tag * as specified in type and the HTML attributes given in options * (without subscriber options). * * @param {Object} options * @return {Object} The created object */ defaultType : function (options) { return $("<" + options.type + ">").dform('attr', options); }, /** * Return all types. * * @params {String} name (optional) If passed return * all type generators for a given name. * @return {Object} Mapping from type name to * an array of generator functions. */ types : function (name) { return name ? _types[name ] : _types; }, /** * Register an element type function. * * @param {String|Array} data Can either be the name of the type * function or an object that contains name : type function pairs * @param {Function} fn The function that creates a new type element */ addType : addToObject(_types), /** * Returns all subscribers or all subscribers for a given name. * * @params {String} name (optional) If passed return all * subscribers for a given name * @return {Object} Mapping from subscriber names * to an array of subscriber functions. */ subscribers : function (name) { return name ? _subscriptions[name] : _subscriptions; }, /** * Register a subscriber function. * * @param {String|Object} data Can either be the name of the subscriber * function or an object that contains name : subscriber function pairs * @param {Function} fn The function to subscribe or nothing if an object is passed for data * @param {Array} deps An optional list of dependencies */ subscribe : addToObject(_subscriptions), /** * Returns if a subscriber function with the given name * has been registered. * * @param {String} name The subscriber name * @return {Boolean} True if the given name has at least one subscriber registered, * false otherwise */ hasSubscription : function (name) { return _subscriptions[name] ? true : false; }, /** * Create a new element. * * @param {Object} options - The options to use * @return {Object} The element as created by the builder function specified * or returned by the defaultType function. */ createElement : function (options) { if (!options.type) { throw "No element type given! Must always exist."; } var type = options.type, element = null, // We don't need the type key in the options opts = $.withoutKeys(options, ["type"]); if (_types[type]) { // Run all type element builder functions called typename each(_types[type], function (i, sfn) { element = sfn.call(element, opts); }); } else { // Call defaultType function if no type was found element = $.dform.defaultType(options); } return $(element); }, methods : { /** * Run all subscriptions with the given name and options * on an element. * * @param {String} name The name of the subscriber function * @param {Object} options ptions for the function * @param {String} type The type of the current element as in the registered types * @return {Object} The jQuery object */ run : function (name, options, type) { if (typeof name !== 'string') { return runAll.call(this, name); } return runSubscription.call(this, name, options, type); }, /** * Creates a form element on an element with given options * * @param {Object} options The options to use * @return {Object} The jQuery element this function has been called on */ append : function (options, converter) { if (converter && $.dform.converters && $.isFunction($.dform.converters[converter])) { options = $.dform.converters[converter](options); } // Create element (run builder function for type) var element = $.dform.createElement(options); this.append(element); // Run all subscriptions element.dform('run', options); }, /** * Adds HTML attributes to the current element from the given options. * Any subscriber will be omitted so that the attributes will contain any * key value pair where the key is not the name of a subscriber function * and is not in the string array excludes. * * @param {Object} object The attribute object * @param {Array} excludes A list of keys that should also be excluded * @return {Object} The jQuery object of the this reference */ attr : function (object, excludes) { // Ignore any subscriber name and the objects given in excludes var ignores = $.keyset(_subscriptions); isArray(excludes) && $.merge(ignores, excludes); this.attr($.withoutKeys(object, ignores)); }, /** * * * @param params * @param success * @param error */ ajax : function (params, success, error) { var options = { error : error, url : params }, self = this; if (typeof params !== 'string') { $.extend(options, params); } options.success = function (data) { var callback = success || params.success; self.dform(data); if(callback) { callback.call(self, data); } } $.ajax(options); }, /** * * * @param options */ init : function (options, converter) { var opts = options.type ? options : $.extend({ "type" : "form" }, options); if (converter && $.dform.converters && $.isFunction($.dform.converters[converter])) { opts = $.dform.converters[converter](opts); } if (this.is(opts.type)) { this.dform('attr', opts); this.dform('run', opts); } else { this.dform('append', opts); } } } } }); /** * The jQuery plugin function * * @param options The form options * @param {String} converter The name of the converter in $.dform.converters * that will be used to convert the options */ $.fn.dform = function (options, converter, error) { var self = $(this); if ($.dform.methods[options]) { $.dform.methods[options].apply(self, Array.prototype.slice.call(arguments, 1)); } else { if (typeof options === 'string') { $.dform.methods.ajax.call(self, { url : options, dataType : 'json' }, converter, error); } else { $.dform.methods.init.apply(self, arguments); } } return this; } })(jQuery); /* * jQuery dform plugin * Copyright (C) 2012 David Luecke <daff@neyeon.com>, [http://daffl.github.com/jquery.dform] * * Licensed under the MIT license */ (function ($) { var each = $.each, _element = function (tag, excludes) { return function (ops) { return $(tag).dform('attr', ops, excludes); }; }, _html = function (options, type) { var self = this; if ($.isPlainObject(options)) { self.dform('append', options); } else if ($.isArray(options)) { each(options, function (index, nested) { self.dform('append', nested); }); } else { self.html(options); } }; $.dform.addType({ container : _element("<div>"), text : _element('<input type="text" />'), password : _element('<input type="password" />'), submit : _element('<input type="submit" />'), reset : _element('<input type="reset" />'), hidden : _element('<input type="hidden" />'), radio : _element('<input type="radio" />'), checkbox : _element('<input type="checkbox" />'), file : _element('<input type="file" />'), number : _element('<input type="number" />'), url : _element('<input type="url" />'), tel : _element('<input type="tel" />'), email : _element('<input type="email" />'), checkboxes : _element("<div>", ["name"]), radiobuttons : _element("<div>", ["name"]) }); $.dform.subscribe({ /** * Adds a class to the current element. * Ovverrides the default behaviour which would be replacing the class attribute. * * @param options A list of whitespace separated classnames * @param type The type of the *this* element */ "class" : function (options, type) { this.addClass(options); }, /** * Sets html content of the current element * * @param options The html content to set as a string * @param type The type of the *this* element */ "html" : _html, /** * Recursively appends subelements to the current form element. * * @param options Either an object with key value pairs * where the key is the element name and the value the * subelement options or an array of objects where each object * is the options for a subelement * @param type The type of the *this* element */ "elements" : _html, /** * Sets the value of the current element. * * @param options The value to set * @param type The type of the *this* element */ "value" : function (options) { this.val(options); }, /** * Set CSS styles for the current element * * @param options The Styles to set * @param type The type of the *this* element */ "css" : function (options) { this.css(options); }, /** * Adds options to select type elements or radio and checkbox list elements. * * @param options A key value pair where the key is the * option value and the value the options text or the settings for the element. * @param type The type of the *this* element */ "options" : function (options, type) { var self = this; // Options for select elements if ((type === "select" || type === "optgroup") && typeof options !== 'string') { each(options, function (value, content) { var option = { type : 'option', value : value }; if (typeof (content) === "string") { option.html = content; } if (typeof (content) === "object") { option = $.extend(option, content); } self.dform('append', option); }); } else if (type === "checkboxes" || type === "radiobuttons") { // Options for checkbox and radiobutton lists each(options, function (value, content) { var boxoptions = ((type === "radiobuttons") ? { "type" : "radio" } : { "type" : "checkbox" }); if (typeof(content) === "string") { boxoptions["caption"] = content; } else { $.extend(boxoptions, content); } boxoptions["value"] = value; self.dform('append', boxoptions); }); } }, /** * Adds caption to elements. * * Depending on the element type the following elements will * be used: * - A legend for <fieldset> elements * - A <label> next to <radio> or <checkbox> elements * - A <label> before any other element * * @param options A string for the caption or the options for the * @param type The type of the *this* element */ "caption" : function (options, type) { var ops = {}; if (typeof (options) === "string") { ops["html"] = options; } else { $.extend(ops, options); } if (type == "fieldset") { // Labels for fieldsets are legend ops.type = "legend"; this.dform('append', ops); } else { ops.type = "label"; if (this.attr("id")) { ops["for"] = this.attr("id"); } var label = $($.dform.createElement(ops)); if (type === "checkbox" || type === "radio") { this.parent().append($(label)); } else { label.insertBefore(this); } label.dform('run', ops); } }, /** * The subscriber for the type parameter. * Although the type parameter is used to get the correct element * type it is just treated as a simple subscriber otherwise. * Since every element needs a type * parameter feel free to add other type subscribers to do * any processing between [pre] and [post]. * * This subscriber adds the auto generated classes according * to the type prefix in $.dform.options.prefix. * * @param options The name of the type * @param type The type of the *this* element */ "type" : function (options, type) { if ($.dform.options.prefix) { this.addClass($.dform.options.prefix + type); } }, /** * Retrieves JSON data from a URL and creates a sub form. * * @param options * @param type */ "url" : function (options) { this.dform('ajax', options); }, /** * Post processing function, that will run whenever all other subscribers are finished. * * @param options All options that have been used for * @param type The type of the *this* element */ "[post]" : function (options, type) { if (type === "checkboxes" || type === "radiobuttons") { var boxtype = ((type === "checkboxes") ? "checkbox" : "radio"); this.children("[type=" + boxtype + "]").each(function () { $(this).attr("name", options.name); }); } } }); })(jQuery); /* * jQuery dform plugin * Copyright (C) 2012 David Luecke <daff@neyeon.com>, [http://daffl.github.com/jquery.dform] * * Licensed under the MIT license */ (function($) { var _getOptions = function(type, options) { return $.withKeys(options, $.keyset($.ui[type]["prototype"]["options"])); }, _get = function(keys, obj) { for(var item = obj, i = 0; i < keys.length; i++) { item = item[keys[i]]; if(!item) { return null; } } return item; } $.dform.addType("progressbar", /** * Returns a jQuery UI progressbar. * * @param options As specified in the jQuery UI progressbar documentation at * http://jqueryui.com/demos/progressbar/ */ function(options) { return $("<div>").dform('attr', options).progressbar(_getOptions("progressbar", options)); }, $.isFunction($.fn.progressbar)); $.dform.addType("slider", /** * Returns a slider element. * * @param options As specified in the jQuery UI slider documentation at * http://jqueryui.com/demos/slider/ */ function(options) { return $("<div>").dform('attr', options).slider(_getOptions("slider", options)); }, $.isFunction($.fn.slider)); $.dform.addType("accordion", /** * Creates an element container for a jQuery UI accordion. * * @param options As specified in the jQuery UI accordion documentation at * http://jqueryui.com/demos/accordion/ */ function(options) { return $("<div>").dform('attr', options); }, $.isFunction($.fn.accordion)); $.dform.addType("tabs", /** * Returns a container for jQuery UI tabs. * * @param options The options as in jQuery UI tab */ function(options) { return $("<div>").dform('attr', options); }, $.isFunction($.fn.tabs)); $.dform.subscribe("entries", /** * Create entries for the accordion type. * Use the <elements> subscriber to create subelements in each entry. * * @param options All options for the container div. The <caption> will be * turned into the accordion or tab title. * @param type The type. This subscriber will only run for accordion */ function(options, type) { if(type == "accordion") { var scoper = this; $.each(options, function(index, options) { var el = $.extend({ "type" : "div" }, options); $(scoper).dform('append', el); if(options.caption) { var label = $(scoper).children("div:last").prev(); label.replaceWith('<h3><a href="#">' + label.html() + '</a></h3>'); } }); } }, $.isFunction($.fn.accordion)); $.dform.subscribe("entries", /** * Create entries for the accordion type. * Use the <elements> subscriber to create subelements in each entry. * * @param options All options for the container div. The <caption> will be * turned into the accordion or tab title. * @param type The type. This subscriber will only run for accordion */ function(options, type) { if(type == "tabs") { var scoper = this; this.append("<ul>"); var ul = $(scoper).children("ul:first"); $.each(options, function(index, options) { var id = options.id ? options.id : index; $.extend(options, { "type" : "container", "id" : id }); $(scoper).dform('append', options); var label = $(scoper).children("div:last").prev(); $(label).wrapInner($("<a>").attr("href", "#" + id)); $(ul).append($("<li>").wrapInner(label)); }); } }, $.isFunction($.fn.tabs)); $.dform.subscribe("dialog", /** * Turns an element into a jQuery UI dialog. * * @param options As specified in the [jQuery UI dialog documentation\(http://jqueryui.com/demos/dialog/) */ function(options) { this.dialog(options); }, $.isFunction($.fn.dialog)); $.dform.subscribe("resizable", /** * Make the current element resizable. * * @param options As specified in the [jQuery UI resizable documentation](http://jqueryui.com/demos/resizable/) */ function(options) { this.resizable(options); }, $.isFunction($.fn.resizable)); $.dform.subscribe("datepicker", /** * Adds a jQuery UI datepicker to an element of type text. * * @param options As specified in the [jQuery UI datepicker documentation](http://jqueryui.com/demos/datepicker/) * @param type The type of the element */ function(options, type) { if (type == "text") { this.datepicker(options); } }, $.isFunction($.fn.datepicker)); $.dform.subscribe("autocomplete", /** * Adds the autocomplete feature to a text element. * * @param options As specified in the [jQuery UI autotomplete documentation](http://jqueryui.com/demos/autotomplete/) * @param type The type of the element */ function(options, type) { if (type == "text") { this.autocomplete(options); } }, $.isFunction($.fn.autocomplete)); $.dform.subscribe("[post]", /** * Post processing subscriber that adds jQuery UI styling classes to * text, textarea, password and fieldset elements as well * as calling .button() on submit or button elements. * * Additionally, accordion and tabs elements will be initialized * with their options. * * @param options All options that have been passed for creating the element * @param type The type of the element */ function(options, type) { if (this.parents("form").hasClass("ui-widget")) { if ((type === "button" || type === "submit") && $.isFunction($.fn.button)) { this.button(); } if (!!~$.inArray(type, [ "text", "textarea", "password", "fieldset" ])) { this.addClass("ui-widget-content ui-corner-all"); } } if(type === "accordion" || type === "tabs") { this[type](_getOptions(type, options)); } }); $.dform.subscribe("[pre]", /** * Add a preprocessing subscriber that calls .validate() on the form, * so that we can add rules to the input elements. Additionally * the jQuery UI highlight classes will be added to the validation * plugin default settings if the form has the ui-widget class. * * @param options All options that have been used for * creating the current element. * @param type The type of the *this* element */ function(options, type) { if(type == "form") { var defaults = {}; if(this.hasClass("ui-widget")) { defaults = { highlight: function(input) { $(input).addClass("ui-state-highlight"); }, unhighlight: function(input) { $(input).removeClass("ui-state-highlight"); } }; } if (typeof (options.validate) == 'object') { $.extend(defaults, options.validate); } this.validate(defaults); } }, $.isFunction($.fn.validate)); /** * Adds support for the jQuery validation rulesets. * For types: text, password, textarea, radio, checkbox sets up rules through rules("add", rules) for validation plugin * For type <form> sets up as options object for validate method of validation plugin * For rules of types checkboxes and radiobuttons you should use this subscriber for type form (to see example below) * * @param options * @param type */ $.dform.subscribe("validate", function(options, type) { if (type != "form") { this.rules("add", options); } }, $.isFunction($.fn.validate)); $.dform.subscribe("ajax", /** * If the current element is a form, it will be turned into a dynamic form * that can be submitted asynchronously. * * @param options Options as specified in the [jQuery Form plugin documentation](http://jquery.malsup.com/form/#options-object) * @param type The type of the element */ function(options, type) { if(type === "form") { this.ajaxForm(options); } }, $.isFunction($.fn.ajaxForm)); $.dform.subscribe('html', /** * Extends the html subscriber that will replace any string with it's translated * equivalent using the jQuery Global plugin. The html content will be interpreted * as an index string where the first part indicates the localize main index and * every following a sub index using getValueAt. * * @param options The dot separated html string to localize * @param type The type of the this element */ function(options, type) { if(typeof options === 'string') { var keys = options.split('.'), translated = Globalize.localize(keys.shift()); if(translated = _get(keys, translated)) { $(this).html(translated); } } }, typeof Globalize !== 'undefined' && $.isFunction(Globalize.localize)); $.dform.subscribe('options', /** * Extends the options subscriber for using internationalized option * lists. * * @param options Options as specified in the <jQuery Form plugin documentation at http://jquery.malsup.com/form/#options-object> * @param type The type of the element. */ function(options, type) { if(type === 'select' && typeof(options) === 'string') { $(this).html(''); var keys = options.split('.'), optlist = Globalize.localize(keys.shift()); if(optlist = _get(keys, optlist)) { $(this).dform('run', 'options', optlist, type); } } }, typeof Globalize !== 'undefined' && $.isFunction(Globalize.localize)); })(jQuery);