initial commit

This commit is contained in:
elclanrs 2013-10-03 20:45:36 -04:00
commit 23892b6625
36 changed files with 3969 additions and 0 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
*~
*.swp
node_modules/

647
README.md Normal file
View file

@ -0,0 +1,647 @@
![Ideal Forms logo](http://i.imgur.com/F9erFXl.png)
The best forms just got better! Ideal Forms 3 is smaller, faster, and more extensible.
**Support:** IE9+ and all modern browsers.
**Demo:**
### Major changes since version 2
Ideal Forms 3 is **not** compatible with version 2. Here's what's new:
- New architecture, more modularity
- Custom markup
- Extensions
- Improved custom checkbox, radio and file inputs
- Improved built-in theme
- Switch to Stylus
- Drop custom select menu
- Drop support for IE8
## Table of Contents
- [Setup](#setup)
- [Options](#options)
- [Markup](#markup)
- [Custom Markup](#custom-markup)
- [Adding Rules](#adding-rules)
- [Built-in Rules](#built-in-rules)
- [Methods](#methods)
- [Built-in Extensions](#built-in-extensions)
- [Dynamic Fields](#extension-dynamic-fields)
- [Steps](#extension:-steps)
- [Custom Inputs](#extension-custom-inputs)
- [Ajax](#extension-ajax)
- [Datepicker](#extension-datepicker)
- [Custom Rules](#custom-rules)
- [Custom Extensions](#custom-extensions)
- [Themes](#themes)
- [Build & Share](#build--share)
## Setup
- Load latest [jQuery](http://code.jquery.com/jquery-2.0.3.min.js) library
- Load `css/jquery.idealforms.css` stylesheet
- Load `js/out/jquery.idealforms.min.js` plugin
- For better IE support, replace the opening `html` tag with:
```html
<!--[if IE 9]> <html class="ie9" lang="en"> <![endif]-->
<!--[if (gt IE 9)|!(IE)]><!--> <html lang="en"> <!--<![endif]-->
```
- Call Ideal Forms on your form:
```javascript
$('form').idealforms({ options });
```
## Options
```javascript
defaults = {
field: '.field',
error: '.error',
iconHtml: '<i/>',
iconClass: 'icon',
invalidClass: 'invalid',
validClass: 'valid',
silentLoad: true,
onValidate: $.noop,
onSubmit: $.noop,
rules: {}
}
```
### field
The field container for [custom markup](#custom-markup).
### error
The error container for custom markup.
### iconHtml
The element to use as icon. Set to `false` to disable icons.
### iconClass
The class for the icon element.
### invalidClass
The class that will be added to invalid fields.
### validClass
The class that will be added to valid fields.
### silentLoad
Initialize the form silently, otherwise focus the first invalid input.
### onValidate(input, rule, valid)
Callback that runs after an input attempts to validate.
- **input:** The input being validated
- **rule:** The rule that the input is validating.
- **valid:** Boolean. Did it validate?
### onSubmit(invalid, event)
Callback that runs when the form is submitted.
- **invalid:** The number of invalid fields if any.
- **event:** The submit event (prevented by default).
## Markup
You can get started quickly using Ideal Forms' default markup:
```html
<form class="idealforms" novalidate action="/" method="post">
<!-- Text -->
<div class="field">
<label class="main">Username:</label>
<input name="username" type="text">
<span class="error"></span>
</div>
<!-- File -->
<div class="field">
<label class="main">Picture:</label>
<input id="picture" name="picture" type="file" multiple>
<span class="error"></span>
</div>
<!-- Radio -->
<div class="field">
<label class="main">Sex:</label>
<p class="group">
<label><input name="sex" type="radio" value="male">Male</label>
<label><input name="sex" type="radio" value="female">Female</label>
</p>
<span class="error"></span>
</div>
<!-- Checkbox -->
<div class="field">
<label class="main">Hobbies:</label>
<p class="group">
<label><input name="hobbies[]" type="checkbox" value="football">Football</label>
<label><input name="hobbies[]" type="checkbox" value="basketball">Basketball</label>
<label><input name="hobbies[]" type="checkbox" value="dancing">Dancing</label>
</p>
<span class="error"></span>
</div>
<!-- Select -->
<div class="field">
<label class="main">Options:</label>
<select name="options" id="">
<option value="default">&ndash; Select an option &ndash;</option>
<option value="1">One</option>
<option value="2">Two</option>
</select>
<span class="error"></span>
</div>
<!-- Textarea -->
<div class="field">
<label class="main">Comments:</label>
<textarea name="comments" cols="30" rows="10"></textarea>
<span class="error"></span>
</div>
<!-- Button -->
<button type="submit">Submit</button>
</form>
```
### Custom Markup
Ideal Forms 3 has been built from scratch with flexibility in mind. The markup is no longer tied to the plugin. If the default markup doesn't work for you, you can create your own markup. Ideal Forms will look for the following:
- **A field:** A field must contain at least a label, an input and an error.
- **A label:** The label to identify the field.
- **An input or group of inputs:** Must be a single input or multiple related inputs such as checkboxes or radios. A field _cannot_ contain inputs with different `name` attributes; this is a limitation by design.
- **An error container:** An element to hold the error.
Then you have to tell Ideal Forms that you want to use custom markup. You can specify these options when initializing the plugin:
```javascript
$('form').idealforms({
field: '.myfield', // selector
error: '.myerror', // selector
validClass: 'myvalid', // class name
invalidClass: 'myinvalid' // class name
});
```
## Adding Rules
Pass an object to the `rules` option, where each key corresponds to a `name` attribute and each value is a string of rules assigned to that input. Always quote keys for consistency:
```javascript
$('form').idealforms({
rules: {
'username': 'required username',
'password': 'required password',
'sex': 'minoption:1',
'hobbies[]': 'minoption:1 maxoption:2',
'options': 'select:default'
}
});
```
You can also add rules after initializing the plugin:
```javascript
$('form').idealforms('addRules', {
'comments': 'required minmax:50:200'
});
```
## Built-in Rules
A rule must be in this format `rule:param` where `rule` is the name of the `rule` and `param` is a rule parameter, for example `minmax:10:50` will use the `minmax` rule with two arguments, `10` and `50`.
- **required:** The field is required. Only works with text inputs.
- **digits:** Only digits.
- **number:** Must be a number.
- **username:** Must be between 4 and 32 characters long and start with a letter. You may use letters, numbers, underscores, and one dot.
- **email:** Must be a valid email.
- **pass:** Must be at least 6 characters long, and contain at least one number, one uppercase and one lowercase letter.
- **strongpass:** Must be at least 8 characters long and contain at least one uppercase and one lowercase letter and one number or special character.
- **phone:** Must be a valid US phone number.
- **zip:** Must be a valid US zip code
- **url:** Must be a valid URL.
- **range:min:max:** Must be a number between `min` and `max`. Usually combined with `number` or `digits`.
- **min:min:** Must be at least `min` characters long.
- **max:max:** Must be no more that `max` characters long.
- **minmax:min:max:** Must be between `min` and `max` characters long.
- **minoption:min:** Must have at least `min` checkboxes or radios selected.
- **maxoption:max:** Must have no more than `max` checkboxes or radios selected.
- **select:default:** Make a select required, where `default` is the value of the default option.
- **extension:ext:** Validates file inputs. You can have as many `ext` as you want.
- **equalto:name:** Must be equal to another field where `name` is the name of the field.
- **ajax:** See the built-in [Ajax Extension](#ajax).
## Methods
To call a method run `idealforms` on the form and pass the method and arguments:
```javascript
$('form').idealforms('method', arg1, arg2, ...);
```
### .idealforms('addRules', rules)
See [Adding Rules](#adding-rules)
### .idealforms('get:invalid')
Returns a jQuery collection of all invalid fields. Access the `length` to check how many fields are invalid.
### .idealforms('focusFirstInvalid')
Focus the first invalid field.
### .idealforms('is:valid', name)
Check if the input with `name` attribute is valid.
### .idealforms('reset')
Reset all onputs to zero. That means emptying all the values of text inputs, unchecking all checkboxes and radios, and reverting selects to their default option.
## Built-in Extensions
Ideal Forms 3 has been re-designed to be able to extend the core easily. Read on [Custom Extensions](#custom-extensions) to learn more.
Ideal Forms comes with a few built-in extensions. Extensions can be disabled with the `disabledExtensions` option by passing a space separated string of extensions.
```javascript
$('form').idealforms({
disabledExtensions: 'dynamicFields steps customInputs ajax'
});
```
### Extension: Dynamic Fields
Dynamic Fields extends core with the following methods:
#### .idealforms('addFields', fields)
Add fields to the form.
- **fields:** And object where each key corresponds to the `name` attribute. The value of the object is another object that can contain any of the following options (* are required):
```javascript
{
type*: 'text:subtype | file | group:subtype | select',
label*: 'Label',
value: 'value',
attrs: 'attr="value"',
after: 'name',
before: 'name',
list: [
{ value: 'value', text: 'text' }
...
],
rules: 'rule rule rule'
}
```
Text subtypes include all HMTL5 text types, such as `email`, `number`, etc...
Group subtypes include `radio` and `checkbox`.
If `before` or `after` are not set, the field will be added at the end of the form.
**Example:**
```javascript
$('form').idealforms('addFields', {
'fruits[]': {
type: 'group:checkbox',
label: 'Fruits',
list: [
{ value: 'apple', text: 'Apple', attrs: 'checked' },
{ value: 'orange', text: 'Orange' },
{ value: 'banana', text: 'Banana' }
],
rules: 'minoption:1 maxoption:2'
}
});
```
The HTML is generated according to built-in templates. If you're using your own custom markup you may need custom templates. Ideal Forms provides a simple templating system to easy the pain. These are the default templates that you may change in the options when calling the plugin. They are listed as real HTML for convenience but you must pass a string in the options (you can get the HTML from a script tag for example):
```html
<!-- Base -->
<div class="field" {class}>
<label class="main">{label}</label>
{field}
<span class="error"></span>
</div>
<!-- Text -->
<input name="{name}" type="{subtype}" value="{value}" {attrs}>
<!-- File -->
<input id="{name} "name="{name}" type="file" value="{value}" {attrs}>
<!-- Textarea -->
<textarea name="{name}" {attrs}>{text}</textarea>
<!-- Group -->
<p class="group">
{@list}
<label><input name="{name}" type="{subtype}" value="{#value}" {#attrs}>{#text}</label>
{/list}
</p>
<!-- Select -->
<select name={name}>
{@list}
<option value="{#value}">{#text}</option>
{/list}
</select>
```
```javascript
$('form').idealforms('templates', {
templates: {
base: '...',
text: '...',
file: '...',
textarea: '...',
group: '...',
select: '...'
}
});
```
The templating rules are:
- **{var}:** A variable or property.
- **{@list} html {/list}:** A loop.
- **{#var}:** A loop variable (inside the loop).
#### .idealforms('removeFields', names)
Remove fields from the form.
- **names:** A space separated string of `name` attributes.
**Example:**
```javascript
$('form').idealforms('removeFields', 'username password hobbies[]');
```
#### .idealforms('toggleFields', names)
Show or hide fields. When the fields are hidden they will be excluded from the validation.
- **names:** A space separated string of `name` attributes.
**Example:**
```javascript
$('form').idealforms('toggleFields', 'username password hobbies[]');
```
### Extension: Steps
Steps let you organize your form in sections. Steps expects a container, navigation, wrapper, and at least one step. Using the default options you may build your form like so:
```html
<div class="idealsteps-container">
<nav class="idealsteps-nav"></nav>
<form>
<div class="idealsteps-wrap">
<!-- Step 1 -->
<section class="idealsteps-step">
<!-- Form fields -->
</section>
<!-- Step 2 -->
<section class="idealsteps-step">
<!-- Form fields -->
</section>
<!-- Step 3 -->
<section class="idealsteps-step">
<!-- Form fields -->
</section>
</div>
</form>
</div>
```
Steps adds the following options to Ideal Forms:
```javascript
{
stepsContainer: 'idealsteps-container', // the main container
stepsOptions: { // the options for the Steps extension
nav: '.idealsteps-nav',
navItems: 'li',
// Build nav items as "Step 1", "Step 2"... automatically
// Set to `false` to use your own custom markup
buildNavItems: true,
wrap: '.idealsteps-wrap',
step: '.idealsteps-step',
activeClass: 'idealsteps-step-active',
before: null, // runs before changing steps
after: null, // runs after changing steps
fadeSpeed: 0
}
}
```
Steps provides these methods:
#### .idealforms('goToStep', index)
Go to the step number `index`. Indices start at `0`.
#### .idealforms('prevStep')
Go to the previous step. It wraps around.
#### .idealforms('nextStep')
Go to the next step. It wraps around.
#### .idealforms('firstStep')
Go to the first step.
#### .idealforms('lastStep')
Go to the last step.
### Extension: Custom Inputs
Adds custom checkboxes, radios and file inputs (yes!) seamlessly. The custom select menu has been dropped from Ideal Forms 3; it was proving to be a pain to maintain, and there are much better alternatives, such as [Select2](http://ivaynberg.github.io/select2/) if you need them.
This extension has no additional options or methods.
### Extension: Ajax
Adds an `ajax` rule. First specify a URL on your input:
```html
<input type="text" name="username" data-idealforms-ajax="test-username.php"/>
```
Then add the rule to the field _always_ at last.
```javascript
$('form').idealforms({
rules: {
'username': 'required username ajax'
}
});
```
The ajax filter will read the URL and send a POST request to the server. The server side script must return a JSON encoded `true` (passes validation) or `false` (fails validation). for example in PHP:
```php
<?php
echo json_encode(true);
```
If the response gets delayed the field will switch into "ajax mode" by showing a loading icon until the response comes back.
### Extension: Datepicker
Adds a datepicker to date inputs. You must include [jQuery UI](http://jqueryui.com/) first, then add the class `datepicker` to your date inputs:
```html
<input name="event" type="text" placeholder="mm/dd/yyyy" class="datepicker">
```
Now you can add the `date` rule:
```javascript
$('form').idealforms({
rules: {
'event': 'date'
}
});
```
The default format is `mm/dd/yyyy` for Ideal Forms and `mm/dd/yy` for the jQuery UI datepicker. Note that Ideal Forms represents years with `yyyy` not `yy`, but the formats are interchangeable.
```javascript
$('form').idealforms({
rules: {
'event': 'date:yyyy-mm-dd'
}
});
$('.datepicker').datepicker('option', 'dateFormat', 'yy-mm-dd');
```
## Custom Rules
You can add rules by extending the global `rules` object:
```javascript
$.extend($.idealforms.rules, {
ruleRegex: /abc/g,
// The rule is added as "ruleFunction:arg1:arg2"
// @param input {Node}
ruleFunction: function(input, value, arg1, arg2, ...) {
}
});
```
After you add a rule you must add an error for it, by extending the global `errors` object. Don't forget this step:
```javascript
$.extend($.idealforms.errors, {
ruleRegex: 'Must be a valid value for this rule',
ruleFunction: 'Must be a valid value. {0}, {1}'
});
```
If the rule is a function that takes rule parameters pass the parameters as `{0}`, `{1}`, etc. If you want to print all the parameters use `{*}` where the default separator is a comma but you can use your own like `{*~}` where `~` is the custom separator.
## Custom Extensions
```javascript
$.idealforms.addExtension({
name: 'extensionName',
options: {
option: 'default'
},
methods: {
// @extend
_init: function() {
},
newMethod: function() {
}
}
});
```
## Themes
## FAQ
### How to access the Ideal Forms plugin instance
Ideal Forms attaches an instance of itself to your form(s). To access the instance (prototype) use `data`:
```javascript
var instance = $('form').data('idealforms');
```
Now you an use methods like so:
```javascript
instance.reset(); // reset the form
```
## Build & Share
Ideal Forms 3 is built using some popular NodeJS tools. This is what you'll need to install globally:
- [Stylus](https://github.com/learnboost/stylus)
- [UglifyJS](https://github.com/mishoo/UglifyJS2)
- [Browserify](https://github.com/substack/node-browserify)
Then clone the repo, `cd` into the folder and run `npm install` to install dependencies.
Finally run `watch -c sh compile.sh` to watch for changes and compile. Now you're ready to edit files and help make Ideal Forms better, or create your own fork.
If you want to test ajax make sure to run it on your localhost.
**Enjoy ;)**

9
ajax.php Normal file
View file

@ -0,0 +1,9 @@
<?php
sleep(2);
$users = ['elclanrs', 'peter', 'john', 'mike'];
echo json_encode(! in_array($_POST['username'], $users));
exit;

4
compile.sh Normal file
View file

@ -0,0 +1,4 @@
#!/bin/sh
stylus -c styl/jquery.idealforms.styl -o css/
browserify -d js/main.js > js/out/jquery.idealforms.js
cat js/out/jquery.idealforms.js | uglifyjs --comments -m -c warnings=false -o js/out/jquery.idealforms.min.js

91
css/jquery.idealforms.css Normal file
View file

@ -0,0 +1,91 @@
/*
* jQuery Ideal Forms
* @author: Cedric Ruiz
* @version: 3.0
* @license GPL or MIT
*/
form.idealforms{zoom:1;line-height:1.5;}
form.idealforms:before,form.idealforms:after{content:"";display:table}
form.idealforms:after{clear:both}
form.idealforms *{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}
form.idealforms .field{position:relative;float:left;clear:both;margin:.35em 0}
form.idealforms label.main,form.idealforms .field > input,form.idealforms select,form.idealforms button,form.idealforms textarea,form.idealforms .field .group{float:left}
form.idealforms label.main{width:120px;margin-top:.55em}
form.idealforms input,form.idealforms textarea,form.idealforms select,form.idealforms button,form.idealforms .field .group{margin:0;padding:0;border:0;outline:0;width:290px;padding:.55em;border:1px solid #999;outline:0;background:#fff;-webkit-border-radius:3px;border-radius:3px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.15);box-shadow:inset 0 1px 2px rgba(0,0,0,0.15)}
form.idealforms input{-webkit-transition:background 0.3s ease-in-out;-moz-transition:background 0.3s ease-in-out;-o-transition:background 0.3s ease-in-out;-ms-transition:background 0.3s ease-in-out;transition:background 0.3s ease-in-out}
form.idealforms textarea{width:435px}
form.idealforms select,form.idealforms button{color:#2b2b2b;background:#eee;background:-webkit-linear-gradient(#fff, #ddd);background:-moz-linear-gradient(#fff, #ddd);background:-o-linear-gradient(#fff, #ddd);background:-ms-linear-gradient(#fff, #ddd);background:linear-gradient(#fff, #ddd);border:1px solid #aaa;border-bottom-color:#919191;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.15);box-shadow:0 1px 2px rgba(0,0,0,0.15);-webkit-border-radius:3px;border-radius:3px;padding:.55em 1.5em;}
form.idealforms select:hover,form.idealforms button:hover{background:-webkit-linear-gradient(#fff, #eaeaea);background:-moz-linear-gradient(#fff, #eaeaea);background:-o-linear-gradient(#fff, #eaeaea);background:-ms-linear-gradient(#fff, #eaeaea);background:linear-gradient(#fff, #eaeaea)}
form.idealforms select:active,form.idealforms button:active{background:#ddd}
form.idealforms button{width:auto}
form.idealforms select{padding:.55em;}
form.idealforms select:focus{border:1px solid #444}
form.idealforms input[type="file"]{padding:0}
form.idealforms .field .group{position:relative;padding:1.25em;-webkit-box-shadow:none;box-shadow:none;}
form.idealforms .field .group label{float:left;clear:both;padding:.15em 0;}
form.idealforms .field .group input,form.idealforms .field .group label{margin:0}
form.idealforms .field .group input{width:auto;margin-right:.5em;-webkit-box-shadow:none;box-shadow:none}
form.idealforms .field .group label{margin-right:1em;}
form.idealforms .field .group label:last-of-type{margin:0}
form.idealforms .field.valid input,form.idealforms .field.valid select,form.idealforms .field.valid textarea,form.idealforms .field.valid .group{color:#18445a;background:#edf7fc;border-color:#3f9dcc}
form.idealforms .field.invalid input,form.idealforms .field.invalid select,form.idealforms .field.invalid textarea,form.idealforms .field.invalid .group{color:#430e08;background:#ffeded;border-color:#cc2a18}
form.idealforms .field.valid .group,form.idealforms .field.invalid .group,form.idealforms .field.valid textarea,form.idealforms .field.invalid textarea,form.idealforms .field.valid select,form.idealforms .field.invalid select{color:inherit;background:none}
form.idealforms .field.valid select,form.idealforms .field.invalid select{background:-webkit-linear-gradient(#fff, #ddd);background:-moz-linear-gradient(#fff, #ddd);background:-o-linear-gradient(#fff, #ddd);background:-ms-linear-gradient(#fff, #ddd);background:linear-gradient(#fff, #ddd)}
form.idealforms .field .icon{position:absolute;width:16px;height:16px;top:50%;left:100%;margin-top:-8px;margin-left:8px;background:url("../img/validation.png") -16px 0 no-repeat;cursor:pointer}
form.idealforms .field.invalid .icon{background-position:-16px 0}
form.idealforms .field.valid .icon{background-position:0 0;cursor:default}
form.idealforms .field.invalid .group input,form.idealforms .field.valid .group input{border:0;outline:0;-webkit-box-shadow:none;box-shadow:none}
form.idealforms .field.ajax input{color:#463a09;background:#faf9e8;border-color:#cfaa19}
form.idealforms .field.ajax .icon{background:url("../img/loading.gif")}
form.idealforms .error{display:none;position:absolute;z-index:1;left:100%;top:50%;padding:1em 1.5em;width:193.33333333333334px;margin-left:40px;background:#285d85;background:-webkit-linear-gradient(#285d85, #3070a0);background:-moz-linear-gradient(#285d85, #3070a0);background:-o-linear-gradient(#285d85, #3070a0);background:-ms-linear-gradient(#285d85, #3070a0);background:linear-gradient(#285d85, #3070a0);color:#fff;font-size:85%;font-weight:bold;text-shadow:0 1px 0 rgba(0,0,0,0.3);line-height:1.35;border:1px solid #1c425e;-webkit-border-radius:0 3px 3px 3px;border-radius:0 3px 3px 3px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.15);box-shadow:0 1px 1px rgba(0,0,0,0.15);}
form.idealforms .error:after{content:"";position:absolute;z-index:-1;top:-1px;left:-.7em;border-width:.7em;border-style:solid;border-color:transparent;border-top-color:#285d85}
form.idealforms .idealforms-field-checkbox .icon,form.idealforms .idealforms-field-radio .icon,form.idealforms .idealforms-field-textarea .icon{top:8px;margin-top:0}
form.idealforms .idealforms-field-checkbox .error,form.idealforms .idealforms-field-radio .error,form.idealforms .idealforms-field-textarea .error{top:20px}
.idealsteps-step{zoom:1}
.idealsteps-step:before,.idealsteps-step:after{content:"";display:table}
.idealsteps-step:after{clear:both}
.idealsteps-nav{color:#2b2b2b;background:#eee;background:-webkit-linear-gradient(#fff, #ddd);background:-moz-linear-gradient(#fff, #ddd);background:-o-linear-gradient(#fff, #ddd);background:-ms-linear-gradient(#fff, #ddd);background:linear-gradient(#fff, #ddd);border:1px solid #aaa;border-bottom-color:#919191;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.15);box-shadow:0 1px 2px rgba(0,0,0,0.15);-webkit-border-radius:3px;border-radius:3px;overflow:hidden;margin-bottom:2em;}
.idealsteps-nav ul{margin:0;padding:0;border:0;outline:0;list-style:none}
.idealsteps-nav li{float:left}
.idealsteps-nav a{position:relative;float:left;padding:0 1.5em 0 2.75em;height:3.5em;line-height:3.5em;text-decoration:none;color:#5e5e5e;background:#ddd;-webkit-transition:padding 0.2s ease-in-out;-moz-transition:padding 0.2s ease-in-out;-o-transition:padding 0.2s ease-in-out;-ms-transition:padding 0.2s ease-in-out;transition:padding 0.2s ease-in-out;}
.idealsteps-nav a:focus{outline:0}
.idealsteps-nav a:hover{background:#eaeaea;}
.idealsteps-nav a:hover:after{border-left-color:#eaeaea}
.idealsteps-nav a:after,.idealsteps-nav a:before{content:"";position:absolute;z-index:1;top:0;right:-2em;margin-right:0;margin-top:-.125em;border-width:2em 1em;border-style:solid;border-color:transparent;border-left-color:#ddd}
.idealsteps-nav a:before{margin-right:-1px;border-left-color:#aaa}
.idealsteps-nav li:first-child a{padding-left:1.75em}
.idealsteps-nav li.idealsteps-step-active a{padding-right:3.5em;background:#fff;color:#3f9dcc;font-weight:bold;}
.idealsteps-nav li.idealsteps-step-active a:after{border-left-color:#fff}
.idealsteps-nav li.idealsteps-step-active .counter{opacity:1;-ms-filter:none;filter:none}
.idealsteps-nav .counter{opacity:0;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";filter:alpha(opacity=0);position:absolute;top:50%;right:1em;height:20px;width:20px;margin-top:-.75em;line-height:20px !important;text-align:center;line-height:1;color:#cc2a18;border:1px solid #cc2a18;-webkit-border-radius:10em;border-radius:10em;-webkit-transition:opacity 0.2s ease-in-out;-moz-transition:opacity 0.2s ease-in-out;-o-transition:opacity 0.2s ease-in-out;-ms-transition:opacity 0.2s ease-in-out;transition:opacity 0.2s ease-in-out;}
.idealsteps-nav .counter.zero{color:#3f9dcc;border-color:#3f9dcc}
form.idealforms .ideal-radiocheck-label{cursor:pointer;margin:.15em 0 !important;}
form.idealforms .ideal-radiocheck-label input{float:left}
form.idealforms .ideal-check,form.idealforms .ideal-radio{float:left;margin-right:10px !important;width:20px;height:20px;background:url("../img/radiocheck.png") 0 0}
form.idealforms .ideal-check.focus{background-position:-20px 0}
form.idealforms .ideal-check.checked{background-position:-40px 0}
form.idealforms .ideal-check.checked.focus{background-position:-60px 0}
form.idealforms .ideal-radio{background-position:0 bottom}
form.idealforms .ideal-radio.focus{background-position:-20px bottom}
form.idealforms .ideal-radio.checked{background-position:-40px bottom}
form.idealforms .ideal-radio.checked.focus{background-position:-60px bottom}
form.idealforms .ideal-file-wrap{float:left}
form.idealforms .ideal-file-filename{float:left;width:204px;height:100%;-webkit-border-radius:0;border-radius:0;-webkit-border-top-left-radius:3px;border-top-left-radius:3px;-webkit-border-bottom-left-radius:3px;border-bottom-left-radius:3px;}
form.idealforms .ideal-file-upload{color:#2b2b2b;background:#eee;background:-webkit-linear-gradient(#fff, #ddd);background:-moz-linear-gradient(#fff, #ddd);background:-o-linear-gradient(#fff, #ddd);background:-ms-linear-gradient(#fff, #ddd);background:linear-gradient(#fff, #ddd);border:1px solid #aaa;border-bottom-color:#919191;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.15);box-shadow:0 1px 2px rgba(0,0,0,0.15);-webkit-border-radius:3px;border-radius:3px;padding:.55em 1.5em;overflow:visible;position:relative;float:right;left:-1px;width:87px;padding-left:0;padding-right:0;text-align:center;-webkit-border-radius:0;border-radius:0;-webkit-border-top-right-radius:3px;border-top-right-radius:3px;-webkit-border-bottom-right-radius:3px;border-bottom-right-radius:3px;}
form.idealforms .ideal-file-upload:hover{background:-webkit-linear-gradient(#fff, #eaeaea);background:-moz-linear-gradient(#fff, #eaeaea);background:-o-linear-gradient(#fff, #eaeaea);background:-ms-linear-gradient(#fff, #eaeaea);background:linear-gradient(#fff, #eaeaea)}
form.idealforms .ideal-file-upload:active{background:#ddd}
.ie9 .ideal-file-upload{line-height:1.15}
.idealforms input.datepicker.open{border-bottom-color:transparent;-webkit-border-radius:0;border-radius:0;-webkit-border-top-left-radius:3px;border-top-left-radius:3px;-webkit-border-top-right-radius:3px;border-top-right-radius:3px;}
.ui-datepicker{display:none;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;width:290px;margin-top:-2px;padding:.75em;background:#fff;border:1px solid #999;-webkit-border-bottom-left-radius:3px;border-bottom-left-radius:3px;-webkit-border-bottom-right-radius:3px;border-bottom-right-radius:3px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.2);box-shadow:0 1px 2px rgba(0,0,0,0.2)}
.ui-datepicker-header{position:relative;padding:.2em 0;margin-bottom:.75em;font-weight:bold;}
.ui-datepicker-header .ui-datepicker-title{text-align:center}
.ui-datepicker-header .ui-datepicker-prev,.ui-datepicker-header .ui-datepicker-next{text-indent:-9999px;width:16px;height:16px;position:absolute;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background:url("../img/datepicker.png") 0 0;}
.ui-datepicker-header .ui-datepicker-prev:active,.ui-datepicker-header .ui-datepicker-next:active{margin-top:1px}
.ui-datepicker-header .ui-datepicker-next{background-position:-16px 0}
.ui-datepicker-header .ui-datepicker-prev{left:8px}
.ui-datepicker-header .ui-datepicker-next{right:8px}
.ui-datepicker-calendar{width:100%;border-collapse:collapse;table-layout:fixed;}
.ui-datepicker-calendar td{padding:.25em 0;text-align:center}
.ui-datepicker-calendar a{display:block;text-decoration:none;color:#808080;}
.ui-datepicker-calendar a:hover{color:#3f9dcc;font-weight:bold}
.ui-datepicker-calendar .ui-datepicker-today a{margin:0 .25em;background:#eee;-webkit-border-radius:3px;border-radius:3px}

BIN
img/datepicker.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
img/loading.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
img/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

140
img/logo.svg Normal file
View file

@ -0,0 +1,140 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="744.09448819"
height="1052.3622047"
id="svg2"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="logo.svg">
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1"
inkscape:cx="528.18642"
inkscape:cy="694.15289"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1700"
inkscape:window-height="969"
inkscape:window-x="2328"
inkscape:window-y="234"
inkscape:window-maximized="0"
showborder="false"
inkscape:object-paths="true" />
<defs
id="defs4">
<linearGradient
id="linearGradient3813">
<stop
style="stop-color:#d43c20;stop-opacity:1;"
offset="0"
id="stop3815" />
<stop
style="stop-color:#c32d11;stop-opacity:1;"
offset="1"
id="stop3817" />
</linearGradient>
</defs>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:groupmode="layer"
inkscape:label="Layer 1">
<path
style="fill:#942416;fill-opacity:1;stroke:none"
d="m 330,399.3622 313,0 0,59 -141.47051,0 -15.02949,6.11701 -14.90449,-6.11701 -141.59551,0 z"
id="path3758"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccc"
inkscape:export-filename="/home/elclanrs/Desktop/idealforms3.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" />
<path
inkscape:export-ydpi="90"
inkscape:export-xdpi="90"
inkscape:export-filename="/home/elclanrs/Desktop/idealforms3.png"
sodipodi:nodetypes="cccccccc"
inkscape:connector-curvature="0"
id="rect2989"
d="m 330,395.3622 313,0 0,59 -143.03301,0 L 486.5,464.47921 473.03301,454.3622 330,454.3622 z"
style="fill:#c93929;fill-opacity:1;stroke:none" />
<text
transform="scale(1.0066801,0.99336421)"
inkscape:export-ydpi="90"
inkscape:export-xdpi="90"
inkscape:export-filename="/home/elclanrs/Desktop/idealforms3.png"
sodipodi:linespacing="51.999998%"
id="text2985"
y="438.41376"
x="348.3848"
style="font-size:30.01604652px;font-style:normal;font-weight:normal;line-height:51.99999809%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans"
xml:space="preserve"><tspan
dx="0 0 0 1.0720017 1.0720017 0 0 0 0 0.53600085"
style="font-size:30.01604652px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:51.99999809%;letter-spacing:-1.23065794px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;font-family:Arial Black;-inkscape-font-specification:'Arial Black,'"
y="438.41376"
x="348.3848"
id="tspan2987"
sodipodi:role="line">IDEAL FORMS </tspan></text>
<g
id="g3760"
transform="matrix(1.000000,0.000000,0.000000,1.000000,-1.000000,1.000000)"
inkscape:export-filename="/home/elclanrs/Desktop/idealforms3.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90">
<path
sodipodi:type="arc"
style="fill:#ffffff;fill-opacity:1;stroke:none"
id="path3770"
sodipodi:cx="606.69763"
sodipodi:cy="396.16708"
sodipodi:rx="45.608387"
sodipodi:ry="45.608387"
d="m 652.30602,396.16708 c 0,25.18882 -20.41957,45.60839 -45.60839,45.60839 -25.18881,0 -45.60839,-20.41957 -45.60839,-45.60839 0,-25.18881 20.41958,-45.60838 45.60839,-45.60838 25.18882,0 45.60839,20.41957 45.60839,45.60838 z"
transform="matrix(0.330039,0.000000,0.000000,0.325673,407.000000,295.000000)"
inkscape:export-filename="/home/elclanrs/Desktop/idealforms3.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" />
<text
sodipodi:linespacing="51.999998%"
id="text3766"
y="434.70209"
x="597.02643"
style="font-size:22.03571128999999829px;font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;text-align:start;line-height:51.99999808999999829%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#942416;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu Medium"
xml:space="preserve"
inkscape:export-filename="/home/elclanrs/Desktop/idealforms3.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90"
transform="scale(1.0066801,0.99336422)"><tspan
style="font-size:22.03571128999999829px;font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;text-align:start;line-height:51.99999808999999829%;letter-spacing:-0.90346420000000005px;writing-mode:lr-tb;text-anchor:start;fill:#942416;fill-opacity:1;font-family:Ubuntu;-inkscape-font-specification:Ubuntu Medium"
y="434.70209"
x="597.02643"
id="tspan3768"
sodipodi:role="line">3</tspan></text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.8 KiB

BIN
img/radiocheck.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

BIN
img/validation.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 774 B

208
index.php Normal file
View file

@ -0,0 +1,208 @@
<!DOCTYPE html>
<!--[if lt IE 7 ]> <html lang="en" class="ie6"> <![endif]-->
<!--[if IE 7 ]> <html lang="en" class="ie7"> <![endif]-->
<!--[if IE 8 ]> <html lang="en" class="ie8"> <![endif]-->
<!--[if IE 9 ]> <html lang="en" class="ie9"> <![endif]-->
<!--[if (gt IE 9)|!(IE)]><!--> <html lang="en"> <!--<![endif]-->
<head>
<link rel="stylesheet" href="css/jquery.idealforms.css">
<link rel="stylesheet" href="http://necolas.github.io/normalize.css/2.1.3/normalize.css">
<meta charset=utf-8 />
<title>Ideal Forms 3</title>
<style>
body {
width: 960px;
margin: 2em auto !important;
font: normal 15px/1.5 Arial, sans-serif;
color: #353535;
overflow-y: scroll;
}
.idealforms .submit {
margin: .35em 120px;
}
#invalid {
display: none;
float: left;
padding: .25em .75em;
color: #CC2A18;
font-size: 120%;
font-weight: bold;
}
</style>
</head>
<body>
<div class="idealsteps-container">
<nav class="idealsteps-nav"></nav>
<form action="" novalidate class="idealforms">
<div class="idealsteps-wrap">
<!-- Step 1 -->
<section class="idealsteps-step">
<div class="field">
<label class="main">Username:</label>
<input name="username" type="text" data-idealforms-ajax="ajax.php">
<span class="error"></span>
</div>
<div class="field">
<label class="main">E-Mail:</label>
<input name="email" type="email">
<span class="error"></span>
</div>
<div class="field">
<label class="main">Password:</label>
<input name="password" type="password">
<span class="error"></span>
</div>
<div class="field">
<label class="main">Confirm:</label>
<input name="confirmpass" type="password">
<span class="error"></span>
</div>
<div class="field">
<label class="main">Date:</label>
<input name="date" type="text" placeholder="mm/dd/yyyy" class="datepicker">
<span class="error"></span>
</div>
<div class="field">
<label class="main">Picture:</label>
<input id="picture" name="picture" type="file" multiple>
<span class="error"></span>
</div>
<div class="field">
<label class="main">Website:</label>
<input name="website" type="text">
<span class="error"></span>
</div>
</section>
<!-- Step 2 -->
<section class="idealsteps-step">
<div class="field">
<label class="main">Sex:</label>
<p class="group">
<label><input name="sex" type="radio" value="male">Male</label>
<label><input name="sex" type="radio" value="female">Female</label>
</p>
<span class="error"></span>
</div>
<div class="field">
<label class="main">Hobbies:</label>
<p class="group">
<label><input name="hobbies[]" type="checkbox" value="football">Football</label>
<label><input name="hobbies[]" type="checkbox" value="basketball">Basketball</label>
<label><input name="hobbies[]" type="checkbox" value="dancing">Dancing</label>
<label><input name="hobbies[]" type="checkbox" value="dancing">Parkour</label>
<label><input name="hobbies[]" type="checkbox" value="dancing">Videogames</label>
</p>
<span class="error"></span>
</div>
</section>
<!-- Step 3 -->
<section class="idealsteps-step">
<div class="field">
<label class="main">Phone:</label>
<input name="phone" type="text">
<span class="error"></span>
</div>
<div class="field">
<label class="main">Zip:</label>
<input name="zip" type="text">
<span class="error"></span>
</div>
<div class="field">
<label class="main">Options:</label>
<select name="options" id="">
<option value="default">&ndash; Select an option &ndash;</option>
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
<option value="4">Four</option>
</select>
<span class="error"></span>
</div>
<div class="field">
<label class="main">Comments:</label>
<textarea name="comments" cols="30" rows="10"></textarea>
<span class="error"></span>
</div>
</section>
</div>
<div class="submit">
<button type="submit">Submit</button>
<span id="invalid"></span>
</div>
</form>
</div>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.3/jquery-ui.min.js"></script>
<script src="js/out/jquery.idealforms.js"></script>
<!--<script src="js/out/jquery.idealforms.min.js"></script>-->
<script>
$('form').idealforms({
rules: {
'username': 'required username ajax',
'email': 'required email',
'password': 'required pass',
'confirmpass': 'required equalto:password',
'date': 'date',
'picture': 'required extension:jpg:png',
'website': 'url',
'hobbies[]': 'minoption:1 maxoption:2',
'phone': 'required phone',
'zip': 'required zip',
'options': 'select:default',
},
errors: {
'username': {
ajaxError: 'Username not available'
}
},
onSubmit: function(invalid) {
$('#invalid').show().text(invalid ? (invalid +' invalid fields') : 'All good!')
}
});
$('form').keyup(function() {
$('#invalid').hide();
});
$('form').idealforms('addRules', {
'comments': 'required minmax:50:200'
});
</script>
</body>
</html>

28
js/errors.js Normal file
View file

@ -0,0 +1,28 @@
/**
* Errors
*/
module.exports = {
required: 'This field is required',
digits: 'Must be only digits',
name: 'Must be at least 3 characters long and must only contain letters',
email: 'Must be a valid email',
username: 'Must be at between 4 and 32 characters long and start with a letter. You may use letters, numbers, underscores, and one dot',
pass: 'Must be at least 6 characters long, and contain at least one number, one uppercase and one lowercase letter',
strongpass: 'Must be at least 8 characters long and contain at least one uppercase and one lowercase letter and one number or special character',
phone: 'Must be a valid phone number',
zip: 'Must be a valid zip code',
url: 'Must be a valid URL',
number: 'Must be a number',
range: 'Must be a number between {0} and {1}',
min: 'Must be at least {0} characters long',
max: 'Must be under {0} characters',
minoption: 'Select at least {0} options',
maxoption: 'Select no more than {0} options',
minmax: 'Must be between {0} and {1} characters long',
select: 'Select an option',
extension: 'File(s) must have a valid extension ({*})',
equalto: 'Must have the same value as the "{0}" field',
date: 'Must be a valid date {0}'
};

View file

@ -0,0 +1,61 @@
module.exports = {
name: 'idealAjax',
methods: {
// @extend
_init: function() {
$.extend($.idealforms, { _requests: {} });
$.extend($.idealforms.errors, {
ajax: 'Loading...'
});
$.extend($.idealforms.rules, {
ajax: function(input) {
var self = this
, $field = this._getField(input)
, url = $(input).data('idealforms-ajax')
, userError = $.idealforms._getKey('errors.'+ input.name +'.ajaxError', self.opts)
, requests = $.idealforms._requests
, data = {};
data[input.name] = input.value;
$field.addClass('ajax');
if (requests[input.name]) requests[input.name].abort();
requests[input.name] = $.post(url, data, function(resp) {
if (resp === true) {
$field.data('idealforms-valid', true);
self._handleError(input);
self._handleStyle(input);
} else {
self._handleError(input, userError);
}
$field.removeClass('ajax');
}, 'json');
return false;
}
});
},
// @extend
_validate: function(input, rule) {
if (rule != 'ajax' && $.idealforms._requests[input.name]) {
$.idealforms._requests[input.name].abort();
this._getField(input).removeClass('ajax');
}
}
}
};

View file

@ -0,0 +1,25 @@
require('./idealfile');
require('./idealradiocheck');
module.exports = {
name: 'customInputs',
methods: {
// @extend
_init: function() {
this._buildCustomInputs();
},
addFields: function() {
this._buildCustomInputs();
},
_buildCustomInputs: function() {
this.$form.find(':file').idealfile();
this.$form.find(':checkbox, :radio').idealradiocheck();
}
}
};

View file

@ -0,0 +1,97 @@
/**
* Ideal File
*/
(function($, win, doc, undefined) {
// Browser supports HTML5 multiple file?
var multipleSupport = typeof $('<input/>')[0].multiple !== 'undefined'
, isIE = /msie/i.test(navigator.userAgent)
, plugin = {};
plugin.name = 'idealfile';
plugin.methods = {
_init: function() {
var $file = $(this.el).addClass('ideal-file') // the original file input
, $wrap = $('<div class="ideal-file-wrap">')
, $input = $('<input type="text" class="ideal-file-filename" />')
// Button that will be used in non-IE browsers
, $button = $('<button type="button" class="ideal-file-upload">Open</button>')
// Hack for IE
, $label = $('<label class="ideal-file-upload" for="' + $file[0].id + '">Open</label>');
// Hide by shifting to the left so we
// can still trigger events
$file.css({
position: 'absolute',
left: '-9999px'
});
$wrap.append($input, (isIE ? $label : $button)).insertAfter($file);
// Prevent focus
$file.attr('tabIndex', -1);
$button.attr('tabIndex', -1);
$button.click(function () {
$file.focus().click(); // Open dialog
});
$file.change(function () {
var files = []
, fileArr, filename;
// If multiple is supported then extract
// all filenames from the file array
if (multipleSupport) {
fileArr = $file[0].files;
for (var i = 0, len = fileArr.length; i < len; i++) {
files.push(fileArr[i].name);
}
filename = files.join(', ');
// If not supported then just take the value
// and remove the path to just show the filename
} else {
filename = $file.val().split('\\').pop();
}
$input .val(filename).attr('title', filename);
});
$input.on({
blur: function () {
$file.trigger('blur');
},
keydown: function (e) {
if (e.which === 13) { // Enter
if (!isIE) $file.trigger('click');
$(this).closest('form').one('keydown', function(e) {
if (e.which === 13) e.preventDefault();
});
} else if (e.which === 8 || e.which === 46) { // Backspace & Del
// In IE the value is read-only
// with this trick we remove the old input and add
// a clean clone with all the original events attached
if (isIE) $file.replaceWith($file = $file.clone(true));
$file.val('').trigger('change');
$input.val('');
} else if (e.which === 9) { // TAB
return;
} else { // All other keys
return false;
}
}
});
}
};
require('../../plugin')(plugin);
}(jQuery, window, document));

View file

@ -0,0 +1,48 @@
/*
* idealRadioCheck: jQuery plguin for checkbox and radio replacement
* Usage: $('input[type=checkbox], input[type=radio]').idealRadioCheck()
*/
(function($, win, doc, undefined) {
var plugin = {};
plugin.name = 'idealradiocheck';
plugin.methods = {
_init: function() {
var $input = $(this.el);
var $span = $('<span/>');
$span.addClass('ideal-'+ ($input.is(':checkbox') ? 'check' : 'radio'));
$input.is(':checked') && $span.addClass('checked'); // init
$span.insertAfter($input);
$input.parent('label')
.addClass('ideal-radiocheck-label')
.attr('onclick', ''); // Fix clicking label in iOS
$input.css({ position: 'absolute', left: '-9999px' }); // hide by shifting left
// Events
$input.on({
change: function() {
var $input = $(this);
if ( $input.is('input[type="radio"]') ) {
$input.parent().siblings('label').find('.ideal-radio').removeClass('checked');
}
$span.toggleClass('checked', $input.is(':checked'));
},
focus: function() { $span.addClass('focus') },
blur: function() { $span.removeClass('focus') },
click: function() { $(this).trigger('focus') }
});
}
};
require('../../plugin')(plugin);
}(jQuery, window, document));

View file

@ -0,0 +1,52 @@
module.exports = {
name: 'datepicker',
methods: {
// @extend
_init: function() {
this._buildDatepicker();
},
_buildDatepicker: function() {
var $datepicker = this.$form.find('input.datepicker');
// Always show datepicker below the input
if (jQuery.ui) {
$.datepicker._checkOffset = function(a,b,c){ return b };
}
if (jQuery.ui && $datepicker.length) {
$datepicker.each(function() {
$(this).datepicker({
beforeShow: function(input) {
$(input).addClass('open');
},
onChangeMonthYear: function() {
// Hack to fix IE9 not resizing
var $this = $(this)
, width = $this.outerWidth(); // cache first!
setTimeout(function() {
$this.datepicker('widget').css('width', width);
}, 1);
},
onClose: function() {
$(this).removeClass('open');
}
});
});
// Adjust width
$datepicker.on('focus keyup', function() {
var t = $(this), w = t.outerWidth();
t.datepicker('widget').css('width', w);
});
}
}
}
};

View file

@ -0,0 +1,125 @@
function template(html, data) {
var loop = /\{@([^}]+)\}(.+?)\{\/\1\}/g
, loopVariable = /\{#([^}]+)\}/g
, variable = /\{([^}]+)\}/g;
return html
.replace(loop, function(_, key, list) {
return $.map(data[key], function(item) {
return list.replace(loopVariable, function(_, k) {
return item[k];
});
}).join('');
})
.replace(variable, function(_, key) {
return data[key] || '';
});
}
module.exports = {
name: 'dynamicFields',
options: {
templates: {
base:'\
<div class="field" {class}>\
<label class="main">{label}</label>\
{field}\
<span class="error"></span>\
</div>\
',
text: '<input name="{name}" type="{subtype}" value="{value}" {attrs}>',
file: '<input id="{name} "name="{name}" type="file" {attrs}>',
textarea: '<textarea name="{name}" {attrs}>{text}</textarea>',
group: '\
<p class="group">\
{@list}\
<label><input name="{name}" type="{subtype}" value="{#value}" {#attrs}>{#text}</label>\
{/list}\
</p>\
',
select: '\
<select name={name}>\
{@list}\
<option value="{#value}">{#text}</option>\
{/list}\
</select>\
'
}
},
methods: {
addFields: function(fields) {
var self = this;
$.each(fields, function(name, field) {
var typeArray = field.type.split(':')
, rules = {};
field.name = name;
field.type = typeArray[0];
if (typeArray[1]) field.subtype = typeArray[1];
var html = template(self.opts.templates.base, {
label: field.label,
field: template(self.opts.templates[field.type], field)
});
if (field.after || field.before) {
self.$form.find('[name='+ (field.after || field.before) +']').each(function() {
self._getField(this)[field.after ? 'after' : 'before'](html);
});
} else {
self.$form.find(self.opts.field).last().after(html);
}
if (field.rules) {
rules[name] = field.rules;
self.addRules(rules);
}
});
this._inject('addFields');
},
removeFields: function(names) {
var self = this;
$.each(names.split(' '), function(i, name) {
var $field = self._getField($('[name="'+ name +'"]'));
self.$fields = self.$fields.filter(function() {
return ! $(this).is($field);
});
$field.remove();
});
this._inject('removeFields');
},
toggleFields: function(names) {
var self = this;
$.each(names.split(' '), function(i, name) {
var $field = self._getField($('[name="'+ name +'"]'));
$field.data('idealforms-valid', $field.is(':visible')).toggle();
});
this._inject('toggleFields');
}
}
};

View file

@ -0,0 +1,104 @@
/*!
* Ideal Steps
*/
(function($, win, doc, undefined) {
var plugin = {};
plugin.name = 'idealsteps';
plugin.defaults = {
nav: '.idealsteps-nav',
navItems: 'li',
buildNavItems: true,
wrap: '.idealsteps-wrap',
step: '.idealsteps-step',
activeClass: 'idealsteps-step-active',
before: null,
after: null,
fadeSpeed: 0
};
plugin.methods = {
_init: function() {
var self = this,
active = this.opts.activeClass;
this.$el = $(this.el);
this.$nav = this.$el.find(this.opts.nav);
this.$navItems = this.$nav.find(this.opts.navItems);
this.$wrap = this.$el.find(this.opts.wrap);
this.$steps = this.$wrap.find(this.opts.step);
if (this.opts.buildNavItems) this._buildNavItems();
this.$steps.hide().first().show();
this.$navItems.removeClass(active).first().addClass(active);
this.$navItems.click(function() {
self.go(self.$navItems.index(this));
});
},
_buildNavItems: function() {
var self = this,
isCustom = typeof this.opts.buildNavItems == 'function',
item = function(val){ return '<li><a href="#" tabindex="-1">'+ val +'</a></li>'; },
items;
items = isCustom ?
this.$steps.map(function(i){ return item(self.opts.buildNavItems.call(self, i)) }).get() :
this.$steps.map(function(i){ return item(++i); }).get();
this.$navItems = $(items.join(''));
this.$nav.append($('<ul/>').append(this.$navItems));
},
_getCurIdx: function() {
return this.$steps.index(this.$steps.filter(':visible'));
},
go: function(idx) {
var active = this.opts.activeClass,
fadeSpeed = this.opts.fadeSpeed;
if (typeof idx == 'function') idx = idx.call(this, this._getCurIdx());
if (idx >= this.$steps.length) idx = 0;
if (idx < 0) idx = this.$steps.length-1;
if (this.opts.before) this.opts.before.call(this, idx);
this.$navItems.removeClass(active).eq(idx).addClass(active);
this.$steps.fadeOut(fadeSpeed).eq(idx).fadeIn(fadeSpeed);
if (this.opts.after) this.opts.after.call(this, idx);
},
prev: function() {
this.go(this._getCurIdx() - 1);
},
next: function() {
this.go(this._getCurIdx() + 1);
},
first: function() {
this.go(0);
},
last: function() {
this.go(this.$steps.length-1);
}
};
require('../../plugin')(plugin);
}(jQuery, window, document));

View file

@ -0,0 +1,111 @@
require('./idealsteps');
module.exports = {
name: 'steps',
options: {
stepsContainer: '.idealsteps-container',
stepsOptions: {}
},
methods: {
// @extend
_init: function() {
this._buildSteps();
},
// @extend
_validate: function() {
var self = this;
this._updateSteps();
if ($.idealforms.hasExtension('idealAjax')) {
$.each($.idealforms._requests, function(key, request) {
request.done(function(){ self._updateSteps() });
});
}
},
// @extend
focusFirstInvalid: function(firstInvalid) {
var self = this;
this.$stepsContainer.idealsteps('go', function() {
return this.$steps.filter(function() {
return $(this).find(firstInvalid).length;
}).index();
});
},
_buildSteps: function() {
var self = this, options
, hasRules = ! $.isEmptyObject(this.opts.rules)
, counter = hasRules
? '<span class="counter"/>'
: '<span class="counter zero">0</span>';
options = $.extend({}, {
buildNavItems: function(i){ return 'Step '+ (i+1) + counter }
}, this.opts.stepsOptions);
this.$stepsContainer = this.$form.closest(this.opts.stepsContainer).idealsteps(options);
},
_updateSteps: function() {
var self = this;
this.$stepsContainer.idealsteps('_inject', function() {
var idealsteps = this;
this.$navItems.each(function(i) {
var invalid = idealsteps.$steps.eq(i).find(self.getInvalid()).length;
$(this).find('span').text(invalid).toggleClass('zero', ! invalid);
});
});
},
// @extend
addRules: function() {
this.firstStep();
},
// @extend
toggleFields: function() {
this._updateSteps();
},
// @extend
removeFields: function() {
this._updateSteps();
},
goToStep: function(idx) {
this.$stepsContainer.idealsteps('go', idx);
},
prevStep: function() {
this.$stepsContainer.idealsteps('prev');
},
nextStep: function() {
this.$stepsContainer.idealsteps('next');
},
firstStep: function() {
this.$stepsContainer.idealsteps('first');
},
lastStep: function() {
this.$stepsContainer.idealsteps('last');
}
}
};

61
js/main.js Normal file
View file

@ -0,0 +1,61 @@
/*!
* jQuery Ideal Forms
* @author: Cedric Ruiz
* @version: 3.0
* @license GPL or MIT
*/
(function($, win, doc, undefined) {
var plugin = {};
plugin.name = 'idealforms';
plugin.defaults = {
field: '.field',
error: '.error',
iconHtml: '<i/>',
iconClass: 'icon',
invalidClass: 'invalid',
validClass: 'valid',
silentLoad: true,
onValidate: $.noop,
onSubmit: $.noop
};
plugin.global = {
_format: function(str) {
var args = [].slice.call(arguments, 1);
return str.replace(/\{(\d)\}/g, function(_, match) {
return args[+match] || '';
}).replace(/\{\*([^*}]*)\}/g, function(_, sep) {
return args.join(sep || ', ');
});
},
_getKey: function(key, obj) {
return key.split('.').reduce(function(a,b) {
return a && a[b];
}, obj);
},
ruleSeparator: ' ',
argSeparator: ':',
rules: require('./rules'),
errors: require('./errors'),
extensions: [
require('./extensions/dynamic-fields/dynamic-fields.ext'),
require('./extensions/ajax/ajax.ext'),
require('./extensions/steps/steps.ext'),
require('./extensions/custom-inputs/custom-inputs.ext'),
require('./extensions/datepicker/datepicker.ext')
]
};
plugin.methods = $.extend({}, require('./private'), require('./public'));
require('./plugin')(plugin);
}(jQuery, window, document));

1209
js/out/jquery.idealforms.js Normal file

File diff suppressed because one or more lines are too long

7
js/out/jquery.idealforms.min.js vendored Normal file

File diff suppressed because one or more lines are too long

124
js/plugin.js Normal file
View file

@ -0,0 +1,124 @@
/**
* Plugin boilerplate
*/
module.exports = (function() {
var AP = Array.prototype;
return function(plugin) {
$.extend({
name: 'plugin',
defaults: {},
methods: {},
global: {},
}, plugin);
$[plugin.name] = $.extend({
addExtension: function(extension) {
plugin.global.extensions.push(extension);
},
hasExtension: function(extension) {
return plugin.global.extensions.filter(function(ext) {
return ext.name == extension;
}).length;
}
}, plugin.global);
function Plugin(element, options) {
this.opts = $.extend({}, plugin.defaults, options);
this.el = element;
this._name = plugin.name;
this._init();
}
Plugin._extended = {};
Plugin.prototype._extend = function(extensions) {
var self = this
, disabled = self.opts.disabledExtensions || 'none';
$.each(extensions, function(i, extension) {
$.extend(self.opts, $.extend(true, extension.options, self.opts));
$.each(extension.methods, function(method, fn) {
if (disabled.indexOf(extension.name) > -1) {
return;
}
if (Plugin.prototype[method]) {
Plugin._extended[method] = Plugin._extended[method] || [];
Plugin._extended[method].push({ name: extension.name, fn: fn });
} else {
Plugin.prototype[method] = fn;
}
});
});
};
Plugin.prototype._inject = function(method) {
var args = [].slice.call(arguments, 1);
if (typeof method == 'function') return method.call(this);
var self = this;
if (Plugin._extended[method]) {
$.each(Plugin._extended[method], function(i, plugin) {
plugin.fn.apply(self, args);
});
}
};
Plugin.prototype._init = $.noop;
Plugin.prototype[plugin.name] = function(method) {
if (!method) return this;
try { return this[method].apply(this, AP.slice.call(arguments, 1)); }
catch(e) {}
};
$.extend(Plugin.prototype, plugin.methods);
$.fn[plugin.name] = function() {
var args = AP.slice.call(arguments)
, methodArray = typeof args[0] == 'string' && args[0].split(':')
, method = methodArray[methodArray.length > 1 ? 1 : 0]
, prefix = methodArray.length > 1 && methodArray[0]
, opts = typeof args[0] == 'object' && args[0]
, params = args.slice(1)
, ret;
if (prefix) {
method = prefix + method.substr(0,1).toUpperCase() + method.substr(1,method.length-1);
}
this.each(function() {
var instance = $.data(this, plugin.name);
// Method
if (instance) {
return ret = instance[plugin.name].apply(instance, [method].concat(params));
}
// Init
return $.data(this, plugin.name, new Plugin(this, opts));
});
return prefix ? ret : this;
};
};
}());

174
js/private.js Normal file
View file

@ -0,0 +1,174 @@
/**
* Private methods
*/
module.exports = {
_init: function() {
var self = this;
this._extend($.idealforms.extensions);
this.$form = $(this.el);
this.$fields = $();
this.$inputs = $();
this.$form.submit(function(e) {
e.preventDefault();
self.focusFirstInvalid();
self.opts.onSubmit.call(this, self.getInvalid().length, e);
});
this._inject('_init');
this.addRules(this.opts.rules || {});
if (! this.opts.silentLoad) this.focusFirstInvalid();
},
_buildField: function(input) {
var self = this
, $field = this._getField(input)
, $icon;
$icon = $(this.opts.iconHtml, {
class: this.opts.iconClass,
click: function(){ $(input).focus() }
});
if (! this.$fields.filter($field).length) {
this.$fields = this.$fields.add($field);
if (this.opts.iconHtml) $field.append($icon);
$field.addClass('idealforms-field idealforms-field-'+ input.type);
}
this._addEvents(input);
this._inject('_buildField', input);
},
_addEvents: function(input) {
var self = this
, $field = this._getField(input);
$(input)
.on('change keyup', function(e) {
var oldValue = $field.data('idealforms-value');
if (e.which == 9 || e.which == 16) return;
if (! $(this).is(':checkbox, :radio') && oldValue == this.value) return;
$field.data('idealforms-value', this.value);
self._validate(this, true, true);
})
.focus(function() {
if (self.isValid(this.name)) return false;
if (self._isRequired(this) || this.value) {
$field.find(self.opts.error).show();
}
})
.blur(function() {
$field.find(self.opts.error).hide();
});
},
_isRequired: function(input) {
// We assume non-text inputs with rules are required
if ($(input).is(':checkbox, :radio, select')) return true;
return this.opts.rules[input.name].indexOf('required') > -1;
},
_getRelated: function(input) {
return this._getField(input).find('[name="'+ input.name +'"]');
},
_getField: function(input) {
return $(input).closest(this.opts.field);
},
_getFirstInvalid: function() {
return this.getInvalid().first().find('input:first, textarea, select');
},
_handleError: function(input, error, valid) {
valid = valid || this.isValid(input.name);
var $error = this._getField(input).find(this.opts.error);
this.$form.find(this.opts.error).hide();
if (error) $error.text(error);
$error.toggle(!valid);
},
_handleStyle: function(input, valid) {
valid = valid || this.isValid(input.name);
this._getField(input)
.removeClass(this.opts.validClass +' '+ this.opts.invalidClass)
.addClass(valid ? this.opts.validClass : this.opts.invalidClass)
.find('.'+ this.opts.iconClass).show();
},
_fresh: function(input) {
this._getField(input)
.removeClass(this.opts.validClass +' '+ this.opts.invalidClass)
.find(this.opts.error).hide()
.end()
.find('.'+ this.opts.iconClass).toggle(this._isRequired(input));
},
_validate: function(input, handleError, handleStyle) {
var self = this
, $field = this._getField(input)
, userRules = this.opts.rules[input.name].split($.idealforms.ruleSeparator)
, valid = true
, rule;
// Non-required input with empty value must pass validation
if (! input.value && ! this._isRequired(input)) {
$field.removeData('idealforms-valid');
this._fresh(input);
// Required inputs
} else {
$.each(userRules, function(i, userRule) {
userRule = userRule.split($.idealforms.argSeparator);
rule = userRule[0];
var theRule = $.idealforms.rules[rule]
, args = userRule.slice(1)
, error;
error = $.idealforms._format.apply(null, [
$.idealforms._getKey('errors.'+ input.name +'.'+ rule, self.opts) ||
$.idealforms.errors[rule]
].concat(args));
valid = typeof theRule == 'function'
? theRule.apply(self, [input, input.value].concat(args))
: theRule.test(input.value);
$field.data('idealforms-valid', valid);
if (handleError) self._handleError(input, error, valid);
if (handleStyle) self._handleStyle(input, valid);
self.opts.onValidate.call(self, input, rule, valid);
return valid;
});
}
this._inject('_validate', input, rule, valid);
return valid;
}
};

66
js/public.js Normal file
View file

@ -0,0 +1,66 @@
/**
* Public methods
*/
module.exports = {
addRules: function(rules) {
var self = this;
var $inputs = this.$form.find($.map(rules, function(_, name) {
return '[name="'+ name +'"]';
}).join(','));
$.extend(this.opts.rules, rules);
$inputs.each(function(){ self._buildField(this) });
this.$inputs = this.$inputs
.add($inputs)
.each(function(){ self._validate(this, true) });
this.$fields.find(this.opts.error).hide();
this._inject('addRules');
},
getInvalid: function() {
return this.$fields.filter(function() {
return $(this).data('idealforms-valid') === false;
});
},
focusFirstInvalid: function() {
var firstInvalid = this._getFirstInvalid()[0];
if (firstInvalid) {
this._handleError(firstInvalid);
this._handleStyle(firstInvalid);
this._inject('focusFirstInvalid', firstInvalid);
firstInvalid.focus();
}
},
isValid: function(name) {
if (name) return ! this.getInvalid().find('[name="'+ name +'"]').length;
return ! this.getInvalid().length;
},
reset: function(name) {
var self = this
, $inputs = this.$inputs;
if (name) $inputs = $inputs.filter('[name="'+ name +'"]');
$inputs.filter('input:not(:checkbox, :radio)').val('');
$inputs.filter(':checkbox, :radio').prop('checked', false);
$inputs.filter('select').find('option').prop('selected', function() {
return this.defaultSelected;
});
$inputs.change().each(function(){ self._resetErrorAndStyle(this) });
}
};

102
js/rules.js Normal file
View file

@ -0,0 +1,102 @@
/**
* Rules
*/
module.exports = {
required: /.+/,
digits: /^\d+$/,
email: /^[^@]+@[^@]+\..{2,6}$/,
username: /^[a-z](?=[\w.]{3,31}$)\w*\.?\w*$/i,
pass: /(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{6,}/,
strongpass: /(?=^.{8,}$)((?=.*\d)|(?=.*\W+))(?![.\n])(?=.*[A-Z])(?=.*[a-z]).*$/,
phone: /^[2-9]\d{2}-\d{3}-\d{4}$/,
zip: /^\d{5}$|^\d{5}-\d{4}$/,
url: /^(?:(ftp|http|https):\/\/)?(?:[\w\-]+\.)+[a-z]{2,6}([\:\/?#].*)?$/i,
number: function(input, value) {
return !isNaN(value);
},
range: function(input, value, mix, max) {
return Number(value) >= min && Number(value) <= max;
},
min: function(input, value, min) {
return value.length >= min;
},
max: function(input, value, max) {
return value.length <= max;
},
minoption: function(input, value, min) {
return this._getRelated(input).filter(':checked').length >= min;
},
maxoption: function(input, value, max) {
return this._getRelated(input).filter(':checked').length <= max;
},
minmax: function(input, value, min, max) {
return value.length >= min && value.length <= max;
},
select: function(input, value, def) {
return value != def;
},
extension: function(input) {
var extensions = [].slice.call(arguments, 1)
, valid = false;
$.each(input.files || [{name: input.value}], function(i, file) {
valid = $.inArray(file.name.match(/\.(.+)$/)[1], extensions) > -1;
});
return valid;
},
equalto: function(input, value, target) {
var self = this
, $target = $('[name="'+ target +'"]');
if (this.getInvalid().find($target).length) return false;
$target.off('keyup.equalto').on('keyup.equalto', function() {
self._validate(input, false, true);
});
return input.value == $target.val();
},
date: function(input, value, format) {
format = format || 'mm/dd/yyyy';
var delimiter = /[^mdy]/.exec(format)[0]
, theFormat = format.split(delimiter)
, theDate = value.split(delimiter);
function isDate(date, format) {
var m, d, y;
for (var i = 0, len = format.length; i < len; i++) {
if (/m/.test(format[i])) m = date[i];
if (/d/.test(format[i])) d = date[i];
if (/y/.test(format[i])) y = date[i];
}
if (!m || !d || !y) return false;
return m > 0 && m < 13 &&
y && y.length == 4 &&
d > 0 && d <= (new Date(y, m, 0)).getDate();
}
return isDate(theDate, theFormat);
}
};

7
package.json Normal file
View file

@ -0,0 +1,7 @@
{
"name": "idealforms",
"version": "3.0.0",
"dependencies": {
"nib": "*"
}
}

72
styl/datepicker.styl Normal file
View file

@ -0,0 +1,72 @@
// Datepicker
//---------------------------------------
.idealforms input.datepicker.open
border-bottom-color: transparent
border-radius: 0
border-radius: top radius
.ui-datepicker
display: none
box-sizing: border-box
width: input-width
margin-top: -2px
padding: .75em
background: white
border: 1px solid #999
border-radius: bottom radius
box-shadow: 0 1px 2px rgba(black, .2)
.ui-datepicker-header
position: relative
padding: .2em 0
margin-bottom: .75em
font-weight: bold
.ui-datepicker-title
text-align: center
.ui-datepicker-prev
.ui-datepicker-next
text-indent: -9999px
width: 16px
height: 16px
position: absolute
cursor: pointer
user-select: none
background: url(../img/datepicker.png) 0 0
&:active
margin-top: 1px
.ui-datepicker-next
background-position: -16px 0
.ui-datepicker-prev
left: 8px
.ui-datepicker-next
right: 8px
.ui-datepicker-calendar
width: 100%
border-collapse: collapse
table-layout: fixed
td
padding: .25em 0
text-align: center
a
display: block
text-decoration: none
color: gray
&:hover
color: valid
font-weight: bold
.ui-datepicker-today a
margin: 0 .25em
background: #eee
border-radius: radius

31
styl/idealfile.styl Normal file
View file

@ -0,0 +1,31 @@
// Ideal File
//---------------------------------------
form.idealforms
.ideal-file-wrap
float: left
.ideal-file-filename
float: left
width: input-width*.7 + 1px
height: 100%
border-radius: 0
border-radius: left radius
.ideal-file-upload
idealbutton()
overflow: visible
position: relative
float: right
left: -1px
width: input-width*.3
padding-left: 0
padding-right: 0
text-align: center
border-radius: 0
border-radius: right radius
.ie9
.ideal-file-upload
line-height: 1.15

42
styl/idealradiocheck.styl Normal file
View file

@ -0,0 +1,42 @@
// Ideal Radio & Check
//---------------------------------------
form.idealforms
.ideal-radiocheck-label
cursor: pointer
margin: .15em 0 !important
if group-horizontal
margin: .15em 10px !important
input
float: left
.ideal-check, .ideal-radio
float: left
margin-right: 10px !important
width: 20px
height: 20px
background: url(../img/radiocheck.png) 0 0
.ideal-check.focus
background-position: -20px 0
.ideal-check.checked
background-position: -40px 0
.ideal-check.checked.focus
background-position: -60px 0
.ideal-radio
background-position: 0 bottom
.ideal-radio.focus
background-position: -20px bottom
.ideal-radio.checked
background-position: -40px bottom
.ideal-radio.checked.focus
background-position: -60px bottom

91
styl/idealsteps.styl Normal file
View file

@ -0,0 +1,91 @@
// Ideal Steps
//---------------------------------------
.idealsteps-step
clearfix()
.idealsteps-nav
ui()
overflow: hidden
margin-bottom: 2em
ul
reset-box-model()
list-style: none
li
float: left
a
position: relative
float: left
padding: 0 1.5em 0 2.75em
height: 3.5em
line-height: 3.5em
text-decoration: none
color: darken(ui-element, 50)
background: ui-element
transition: padding .2s ease-in-out
&:focus
outline: 0 // Firefox
&:hover
background: lighten(ui-element, 5)
&:after
border-left-color: lighten(ui-element, 5)
&:after, &:before
content: ""
position: absolute
z-index: 1
top: 0
right: -2em
margin-right: 0
margin-top: -.125em
border-width: 2em 1em
border-style: solid
border-color: transparent
border-left-color: ui-element
&:before
margin-right: -1px
border-left-color: darken(ui-element, 20)
li:first-child a
padding-left: 1.75em
li.idealsteps-step-active
a
padding-right: 3.5em
background: white
color: valid
font-weight: bold
&:after
border-left-color: white
.counter
opacity: 1
.counter
opacity: 0
position: absolute
top: 50%
right: 1em
height: 20px
width: 20px
margin-top: -.75em
line-height: 20px !important
text-align: center
line-height: 1
color: invalid
border: 1px solid invalid
border-radius: 10em
transition: opacity .2s ease-in-out
&.zero
color: valid
border-color: valid

View file

@ -0,0 +1,13 @@
/*!
* jQuery Ideal Forms
* @author: Cedric Ruiz
* @version: 3.0
* @license GPL or MIT
*/
@import '../node_modules/nib'
@import 'vars'
@import 'main'
@import 'idealsteps'
@import 'idealradiocheck'
@import 'idealfile'
@import 'datepicker'

175
styl/main.styl Normal file
View file

@ -0,0 +1,175 @@
// Style
//---------------------------------------
form.idealforms
clearfix()
line-height: 1.5
*
box-sizing: border-box
.field
position: relative
float: left
clear: both
margin: .35em 0
label.main, .field > input, select, button, textarea, .field .group
float: left
label.main
width: label-width
margin-top: .55em
input, textarea, select, button, .field .group
reset-box-model()
width: input-width
padding: .55em
border: 1px solid #999
outline: 0
background: white
border-radius: radius
box-shadow: inset 0 1px 2px rgba(black, .15)
input
transition: background .3s ease-in-out
textarea
width: input-width*1.5
select, button
idealbutton()
button
width: auto
select
padding: .55em
&:focus
border: 1px solid darken(ui-element, 60)
input[type="file"]
padding: 0
.field .group
position: relative
padding: 1.25em
box-shadow: none
label
float: left
clear: both
padding: .15em 0
if group-horizontal
clear: none
input,label
margin: 0
input
width: auto
margin-right: .5em
box-shadow: none
label
margin-right: 1em
&:last-of-type
margin: 0
.field.valid
input, select, textarea, .group
color: darken(valid, 30)
background: valid-bg
border-color: valid
.field.invalid
input, select, textarea, .group
color: darken(invalid, 30)
background: invalid-bg
border-color: invalid
.field.valid, .field.invalid
.group, textarea, select
color: inherit
background: none
select
background: linear-gradient(lighten(ui-element, 15), ui-element)
.field .icon
position: absolute
width: 16px
height: 16px
top: 50%
left: 100%
margin-top: -8px
margin-left: 8px
background: url(../img/validation.png) -16px 0 no-repeat
cursor: pointer
.field.invalid .icon
background-position: -16px 0
.field.valid .icon
background-position: 0 0
cursor: default
.field.invalid, .field.valid
.group input
border: 0
outline: 0
box-shadow: none
.field.ajax
input
color: darken(ajax, 30)
background: ajax-bg
border-color: ajax
.icon
background: url(../img/loading.gif)
.error
display: none
position: absolute
z-index: 1
left: 100%
top: 50%
padding: 1em 1.5em
width: error-width
margin-left: 32 + 8 px
if not icon
margin-left: 1.25em
background: error
background: linear-gradient(error, lighten(error, 7))
color: lighten(error, 90)
font-size: 85%
font-weight: bold
text-shadow: 0 1px 0 rgba(black, .3)
line-height: 1.35
border: 1px solid darken(error, 10)
border-radius: 0 radius radius radius
box-shadow: 0 1px 1px rgba(black, .15)
&:after
content: ""
position: absolute
z-index: -1
top: -1px
left: -.7em
border-width: .7em
border-style: solid
border-color: transparent
border-top-color: error
.idealforms-field-checkbox
.idealforms-field-radio
.idealforms-field-textarea
.icon
top: 8px
margin-top: 0
.error
top: 20px

42
styl/vars.styl Normal file
View file

@ -0,0 +1,42 @@
// Theme
//---------------------------------------
valid = #3F9DCC
valid-bg = #EDF7FC
invalid = #CC2A18
invalid-bg = #FFEDED
ajax = #CFAA19
ajax-bg = #FAF9E8
ui-element = #ddd
error = #285d85
label-width = 120px
input-width = 290px
error-width = (input-width/1.5)
radius = 3px
icon = true
group-horizontal = false
// Mixins
//---------------------------------------
ui()
color: darken(ui-element, 70)
background: #eee
background: linear-gradient(lighten(ui-element, 15), ui-element)
border: 1px solid darken(ui-element, 20)
border-bottom-color: darken(ui-element, 30)
box-shadow: 0 1px 2px rgba(black, .15)
border-radius: radius
idealbutton()
ui()
padding: .55em 1.5em
&:hover
background: linear-gradient(lighten(ui-element, 20), lighten(ui-element, 5))
&:active
background: ui-element