├── assets ├── js │ ├── templates │ │ ├── snippet │ │ │ ├── formname.html │ │ │ ├── button.html │ │ │ ├── filebutton.html │ │ │ ├── textarea.html │ │ │ ├── dateinput.html │ │ │ ├── buttondouble.html │ │ │ ├── multiplecheckboxesinline.html │ │ │ ├── selectbasic.html │ │ │ ├── selectmultiple.html │ │ │ ├── multipleradiosinline.html │ │ │ ├── multiplecheckboxes.html │ │ │ ├── multipleradios.html │ │ │ ├── textinput.html │ │ │ ├── searchinput.html │ │ │ ├── passwordinput.html │ │ │ ├── appendedcheckbox.html │ │ │ ├── appendedtext.html │ │ │ ├── prependedtext.html │ │ │ ├── prependedcheckbox.html │ │ │ ├── buttondropdown.html │ │ │ └── snippet-templates.js │ │ ├── app │ │ │ ├── renderform.html │ │ │ ├── temp.html │ │ │ ├── render.html │ │ │ ├── tab-nav.html │ │ │ └── about.html │ │ └── popover │ │ │ ├── popover-checkbox.html │ │ │ ├── popover-input.html │ │ │ ├── popover-textarea.html │ │ │ ├── popover-textarea-split.html │ │ │ ├── popover-main.html │ │ │ └── popover-select.html │ ├── helper │ │ └── pubsub.js │ ├── lib │ │ ├── Guid.js │ │ ├── build.js │ │ ├── require.js │ │ ├── text.js │ │ └── underscore.js │ ├── data │ │ ├── yaml │ │ │ ├── parse.rb │ │ │ ├── radio.yaml │ │ │ ├── select.yaml │ │ │ ├── buttons.yaml │ │ │ └── input.yaml │ │ ├── radio.json │ │ ├── select.json │ │ ├── buttons.json │ │ └── input.json │ ├── collections │ │ ├── snippets.js │ │ └── my-form-snippets.js │ ├── models │ │ └── snippet.js │ ├── views │ │ ├── tab-snippet.js │ │ ├── tab.js │ │ ├── temp-snippet.js │ │ ├── snippet.js │ │ ├── my-form.js │ │ └── my-form-snippet.js │ ├── main.js │ └── app.js ├── img │ ├── glyphicons-halflings.png │ └── glyphicons-halflings-white.png ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.ttf │ └── glyphicons-halflings-regular.woff └── css │ └── custom.css ├── .gitignore ├── LICENSE ├── package.json ├── DOCUMENTATION.md ├── README.md ├── CONTRIBUTING.md └── index.html /assets/js/templates/snippet/formname.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/js/templates/app/renderform.html: -------------------------------------------------------------------------------- 1 |
2 | <%= text %> 3 |
4 | -------------------------------------------------------------------------------- /assets/js/templates/app/temp.html: -------------------------------------------------------------------------------- 1 |
2 | <%= text %> 3 |
4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.swo 3 | *~ 4 | *.log 5 | node_modules 6 | *.env 7 | .DS_Store 8 | package-lock.json 9 | .bloggify/* 10 | -------------------------------------------------------------------------------- /assets/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bloggify/bootstrap-form-builder/HEAD/assets/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /assets/js/templates/app/render.html: -------------------------------------------------------------------------------- 1 |

Rendered source of your form:

2 | 3 | -------------------------------------------------------------------------------- /assets/js/templates/app/tab-nav.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /assets/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bloggify/bootstrap-form-builder/HEAD/assets/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /assets/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bloggify/bootstrap-form-builder/HEAD/assets/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /assets/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bloggify/bootstrap-form-builder/HEAD/assets/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /assets/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bloggify/bootstrap-form-builder/HEAD/assets/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /assets/js/helper/pubsub.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'jquery', 'underscore', 'backbone' 3 | ], function($, _, Backbone) { 4 | return _.extend({}, Backbone.Events); 5 | }); 6 | -------------------------------------------------------------------------------- /assets/js/lib/Guid.js: -------------------------------------------------------------------------------- 1 | function guidS4(){return(65536*(1+Math.random())|0).toString(16).substring(1)}function generateGuid(){return guidS4()+guidS4()+"-"+guidS4()+"-"+guidS4()+"-"+guidS4()+"-"+guidS4()+guidS4()+guidS4()}; -------------------------------------------------------------------------------- /assets/js/templates/popover/popover-checkbox.html: -------------------------------------------------------------------------------- 1 |
2 | 6 |
7 | -------------------------------------------------------------------------------- /assets/js/templates/popover/popover-input.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 |
6 |
-------------------------------------------------------------------------------- /assets/js/templates/popover/popover-textarea.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 |
6 |
7 | -------------------------------------------------------------------------------- /assets/js/templates/snippet/button.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
5 | 6 |
7 |
8 | -------------------------------------------------------------------------------- /assets/js/templates/snippet/filebutton.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
5 | 6 |
7 |
8 | -------------------------------------------------------------------------------- /assets/js/templates/snippet/textarea.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
5 | 6 |
7 |
8 | -------------------------------------------------------------------------------- /assets/js/templates/snippet/dateinput.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
5 | required <% } %> /> 6 |
7 |
8 | -------------------------------------------------------------------------------- /assets/js/data/yaml/parse.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require "json" 4 | require "yaml" 5 | 6 | yamlfiles = Dir.glob("#{ File.dirname(__FILE__) }/*.yaml") 7 | 8 | yamlfiles.each{ |file| 9 | yaml = YAML.load_file(file) 10 | json = JSON.pretty_generate(YAML.load_file(file)) 11 | File.open("../"+file.gsub("\.yaml", "\.json"), 'w'){ |out| puts "writing #{out.path}"; out.write(json) } 12 | } 13 | -------------------------------------------------------------------------------- /assets/js/templates/popover/popover-textarea-split.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 |
6 |
-------------------------------------------------------------------------------- /assets/js/templates/popover/popover-main.html: -------------------------------------------------------------------------------- 1 |
2 | <% var compiled = _.reduce(items, function(str, v, k){ %> 3 | <% v["name"] = k; %> 4 | <% return str + popoverTemplates[v["type"]](v); %> 5 | <% }, "") %> 6 | <%= compiled %> 7 |
8 |   9 |
10 | -------------------------------------------------------------------------------- /assets/js/templates/popover/popover-select.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 9 |
10 |
11 | -------------------------------------------------------------------------------- /assets/js/templates/snippet/buttondouble.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
5 | 6 | 7 |
8 |
9 | -------------------------------------------------------------------------------- /assets/js/collections/snippets.js: -------------------------------------------------------------------------------- 1 | define([ 2 | "jquery" , "underscore" , "backbone" 3 | , "models/snippet" 4 | , "views/tab-snippet" 5 | ], function( 6 | $, _, Backbone 7 | , SnippetModel 8 | , TabSnippetView 9 | ){ 10 | return Backbone.Collection.extend({ 11 | model: SnippetModel 12 | , renderAll: function(){ 13 | return this.map(function(snippet){ 14 | return new TabSnippetView({model: snippet}).render(); 15 | }); 16 | } 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /assets/js/templates/snippet/multiplecheckboxesinline.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
<% _.each(checkboxes, function(checkbox, i){ %> 5 | <% }); %> 9 |
10 |
11 | -------------------------------------------------------------------------------- /assets/js/templates/snippet/selectbasic.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
"col-md-4" <%}else { %> "<%= inputsize %>"<%}%>> 5 | 8 |
9 |
10 | -------------------------------------------------------------------------------- /assets/js/templates/snippet/selectmultiple.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
"col-md-4" <%}else { %> "<%= inputsize %>"<%}%>> 5 | 8 |
9 |
10 | -------------------------------------------------------------------------------- /assets/js/templates/snippet/multipleradiosinline.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
<% _.each(radios, function(radio, i){ %> 5 | <% }); %> 9 |
10 |
11 | -------------------------------------------------------------------------------- /assets/js/templates/snippet/multiplecheckboxes.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
<% _.each(checkboxes, function(checkbox, i){ %> 5 |
6 | 10 |
<% }); %> 11 |
12 |
13 | -------------------------------------------------------------------------------- /assets/js/templates/snippet/multipleradios.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
<% _.each(radios, function(radio, i){ %> 5 |
6 | 10 |
<% }); %> 11 |
12 |
13 | -------------------------------------------------------------------------------- /assets/js/lib/build.js: -------------------------------------------------------------------------------- 1 | ({ 2 | name: "../main", 3 | out: "../main-built.js" 4 | , shim: { 5 | 'backbone': { 6 | deps: ['underscore', 'jquery'], 7 | exports: 'Backbone' 8 | }, 9 | 'underscore': { 10 | exports: '_' 11 | }, 12 | 'bootstrap': { 13 | deps: ['jquery'], 14 | exports: '$.fn.popover' 15 | } 16 | } 17 | , paths: { 18 | app : ".." 19 | , collections : "../collections" 20 | , data : "../data" 21 | , models : "../models" 22 | , helper : "../helper" 23 | , templates : "../templates" 24 | , views : "../views" 25 | } 26 | }) 27 | -------------------------------------------------------------------------------- /assets/js/templates/snippet/textinput.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
"col-md-8" <%}else { %> "<%= inputsize %>"<%}%> > 5 | 0) { %> aria-describedby="<%= id %>HelpBlock" <% } %>id="<%= id %>" name="<%= id %>" type="text" placeholder="<%= placeholder %>" class="form-control input-md" <% if(required) {%> required <% } %> /> 6 | <% if (helptext.length > 0) { %><%= helptext %><% } %> 7 |
8 |
9 | -------------------------------------------------------------------------------- /assets/js/templates/snippet/searchinput.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
"col-md-4" <%}else { %> "<%= inputsize %>"<%}%> > 5 | 0) { %> aria-describedby="<%= id %>HelpBlock" <% } %>id="<%= id %>" name="<%= id %>" type="password" placeholder="<%= placeholder %>" class="form-control input-md" <% if(required) {%> required <% } %> /> 6 | <% if (helptext.length > 0) { %><%= helptext %><% } %> 7 |
8 |
9 | -------------------------------------------------------------------------------- /assets/js/templates/snippet/passwordinput.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
"col-md-4" <%}else { %> "<%= inputsize %>"<%}%> > 5 | 0) { %> aria-describedby="<%= id %>HelpBlock" <% } %>id="<%= id %>" name="<%= id %>" type="password" placeholder="<%= placeholder %>" class="form-control input-md" <% if(required) {%> required <% } %> /> 6 | <% if (helptext.length > 0) { %><%= helptext %><% } %> 7 |
8 |
9 | -------------------------------------------------------------------------------- /assets/js/collections/my-form-snippets.js: -------------------------------------------------------------------------------- 1 | define([ 2 | "jquery" , "underscore" , "backbone" 3 | , "models/snippet" 4 | , "collections/snippets" 5 | , "views/my-form-snippet" 6 | ], function( 7 | $, _, Backbone 8 | , SnippetModel 9 | , SnippetsCollection 10 | , MyFormSnippetView 11 | ){ 12 | return SnippetsCollection.extend({ 13 | model: SnippetModel 14 | , renderAll: function(){ 15 | return this.map(function(snippet){ 16 | return new MyFormSnippetView({model: snippet}).render(true); 17 | }) 18 | } 19 | , renderAllClean: function(){ 20 | return this.map(function(snippet){ 21 | return new MyFormSnippetView({model: snippet}).render(false); 22 | }); 23 | } 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /assets/js/templates/snippet/appendedcheckbox.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
"col-md-4" <%}else { %> "<%= inputsize %>"<%}%>> 5 |
6 | required <% } %> /> 7 | 8 | checked="checked"<% } %>> 9 | 10 |
11 | <% if (helptext.length > 0) { %>

<%= helptext %>

<% } %> 12 |
13 |
14 | -------------------------------------------------------------------------------- /assets/js/models/snippet.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'jquery', 'underscore', 'backbone' 3 | ], function($, _, Backbone) { 4 | return Backbone.Model.extend({ 5 | getValues: function(){ 6 | return _.reduce(this.get("fields"), function(o, v, k){ 7 | if (v["type"] == "select") { 8 | o[k] = _.find(v["value"], function(o){return o.selected})["value"]; 9 | } else { 10 | o[k] = v["value"]; 11 | } 12 | return o; 13 | }, {}); 14 | } 15 | , idFriendlyTitle: function(){ 16 | return this.get("title").replace(/\W/g,'').toLowerCase(); 17 | } 18 | , setField: function(name, value) { 19 | var fields = this.get("fields"); 20 | fields[name]["value"] = value; 21 | this.set("fields", fields); 22 | } 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /assets/js/templates/snippet/appendedtext.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
"col-md-4" <%}else { %> "<%= inputsize %>"<%}%> > 5 |
6 | 0) { %> aria-describedby="<%= id %>HelpBlock" <% } %>id="<%= id %>" name="<%= id %>" class="form-control" placeholder="<%= placeholder %>" type="text"<% if(required) {%> required <% } %> /> 7 |
<%= append %>
8 |
9 | <% if (helptext.length > 0) { %><%= helptext %><% } %> 10 |
11 |
12 | -------------------------------------------------------------------------------- /assets/js/templates/snippet/prependedtext.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
"col-md-4" <%}else { %> "<%= inputsize %>"<%}%> > 5 |
6 |
<%= prepend %>
7 | 0) { %> aria-describedby="<%= id %>HelpBlock" <% } %>id="<%= id %>" name="<%= id %>" class="form-control" placeholder="<%= placeholder %>" type="text"<% if(required) {%> required <% } %> /> 8 |
9 | <% if (helptext.length > 0) { %><%= helptext %><% } %> 10 |
11 |
12 | -------------------------------------------------------------------------------- /assets/js/views/tab-snippet.js: -------------------------------------------------------------------------------- 1 | define([ 2 | "jquery", "underscore", "backbone" 3 | , "models/snippet" 4 | , "views/snippet", "views/temp-snippet" 5 | , "helper/pubsub" 6 | ], function( 7 | $, _, Backbone 8 | , SnippetModel 9 | , SnippetView, TempSnippetView 10 | , PubSub 11 | ){ 12 | return SnippetView.extend({ 13 | events:{ 14 | "mousedown" : "mouseDownHandler" 15 | } 16 | , mouseDownHandler: function(mouseDownEvent){ 17 | mouseDownEvent.preventDefault(); 18 | mouseDownEvent.stopPropagation(); 19 | //hide all popovers 20 | $(".popover").hide(); 21 | $("body").append(new TempSnippetView({model: new SnippetModel($.extend(true,{},this.model.attributes))}).render()); 22 | PubSub.trigger("newTempPostRender", mouseDownEvent); 23 | } 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /assets/js/main.js: -------------------------------------------------------------------------------- 1 | require.config({ 2 | baseUrl: "assets/js/lib/", 3 | shim: { 4 | 'backbone': { 5 | deps: ['underscore', 'jquery'], 6 | exports: 'Backbone' 7 | }, 8 | 'popper': { 9 | exports: 'Popper' 10 | }, 11 | 'underscore': { 12 | exports: '_' 13 | }, 14 | 'bootstrap': { 15 | deps: ['jquery'], 16 | exports: '$.fn.popover' 17 | } 18 | }, 19 | paths: { 20 | app: "..", 21 | collections: "../collections", 22 | data: "../data", 23 | models: "../models", 24 | helper: "../helper", 25 | templates: "../templates", 26 | views: "../views" 27 | } 28 | }); 29 | 30 | require(["popper.min", 'app/app'], function(popper, app) { 31 | app.initialize(); 32 | }); 33 | -------------------------------------------------------------------------------- /assets/js/templates/snippet/prependedcheckbox.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
"col-md-4" <%}else { %> "<%= inputsize %>"<%}%> > 5 |
6 |
7 | checked="checked"<% } %>> 8 |
9 | 0) { %> aria-describedby="<%= id %>HelpBlock" <% } %>id="<%= id %>" name="<%= id %>" class="form-control" type="text" placeholder="<%= placeholder %>" <% if(required) {%> required <% } %> /> 10 |
11 | <% if (helptext.length > 0) { %><%= helptext %><% } %> 12 |
13 |
14 | -------------------------------------------------------------------------------- /assets/js/templates/snippet/buttondropdown.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
"col-md-4" <%}else { %> "<%= inputsize %>"<%}%>> 5 |
6 |
7 | 15 |
16 |
17 |
18 |
19 | 20 | -------------------------------------------------------------------------------- /assets/js/templates/app/about.html: -------------------------------------------------------------------------------- 1 |

About

2 | 3 |

4 | Originally, created by Adam Moore (@minikomi) to help take the stress out of 5 | writing all that markup to get bootstrap forms together. 🎨 6 |

7 | 8 |

9 | Then, it was updated to work with Bootstrap 3, thanks to Ihab Soliman (@IhabSoliman). 🍰 10 |

11 | 12 |

13 | Finally, updated by @Bloggify to make it compatible with Bootstrap 4. 🚀 14 |

15 | 16 |

17 | If you have a problem, or want a specific snippet added please check out the 18 | GitHub project. 19 | 20 | Adding snippets is quite simple now so please don't hesitate to open an issue! 21 |

22 | 23 |
Versions
24 | 25 | 30 | -------------------------------------------------------------------------------- /assets/js/views/tab.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'jquery', 'underscore', 'backbone' 3 | , "text!templates/app/tab-nav.html" 4 | 5 | ], function($, _, Backbone, 6 | _tabNavTemplate){ 7 | return Backbone.View.extend({ 8 | tagName: "div" 9 | , className: "tab-pane" 10 | , initialize: function() { 11 | this.id = this.options.title.toLowerCase().replace(/\W/g,''); 12 | this.tabNavTemplate = _.template(_tabNavTemplate); 13 | this.render(); 14 | } 15 | , render: function(){ 16 | // Render Snippet Views 17 | var that = this; 18 | if (that.collection !== undefined) { 19 | _.each(this.collection.renderAll(), function(snippet){ 20 | that.$el.append(snippet); 21 | }); 22 | } else if (that.options.content){ 23 | that.$el.append(that.options.content); 24 | } 25 | // Render & append nav for tab 26 | $("#formtabs").append(this.tabNavTemplate({title: this.options.title, id: this.id})) 27 | // Render tab 28 | this.$el.attr("id", this.id); 29 | this.$el.appendTo(".tab-content"); 30 | this.delegateEvents(); 31 | } 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2012-21 Bloggify (https://bloggify.org) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bootstrap-form-builder-v4", 3 | "version": "1.0.4", 4 | "description": "A drag-and-drop form builder for Bootstrap 4.", 5 | "main": "lib/index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "build": "./node_modules/.bin/r.js -o assets/js/lib/build.js" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/Bloggify/bootstrap-form-builder.git" 13 | }, 14 | "keywords": [ 15 | "bootstrap", 16 | "builder", 17 | "drag", 18 | "drop" 19 | ], 20 | "author": "Bloggify (https://bloggify.org)", 21 | "license": "MIT", 22 | "bugs": { 23 | "url": "https://github.com/Bloggify/bootstrap-form-builder/issues" 24 | }, 25 | "homepage": "https://github.com/Bloggify/bootstrap-form-builder#readme", 26 | "devDependencies": { 27 | "requirejs": "^2.3.5" 28 | }, 29 | "files": [ 30 | "bin/", 31 | "app/", 32 | "lib/", 33 | "dist/", 34 | "src/", 35 | "scripts/", 36 | "resources/", 37 | "menu/", 38 | "cli.js", 39 | "index.js", 40 | "bloggify.js", 41 | "bloggify.json" 42 | ], 43 | "blah": { 44 | "title": "Bootstrap v4 Form Builder", 45 | "ex_img": "https://i.imgur.com/cFqF7nY.png", 46 | "ex_url": "https://bloggify.github.io/bootstrap-form-builder" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /DOCUMENTATION.md: -------------------------------------------------------------------------------- 1 | # Bootstrap v 3.0.0 Form Builder 2 | ## What's this? 3 | A Drag-and-drop form builder for [twitter bootstrap](http://getbootstrap.com/). 4 | 5 | ## Where can I see it in action using bootstrap v 2.3.1 thanks to [minikomi](https://github.com/minikomi/Bootstrap-Form-Builder/)? 6 | And It's hosted on github pages [here](http://ihabsoliman.github.io/Bootstrap-Form-Builder) using bootstrap v 3.0.0 7 | 8 | ### Notes 9 | * For development & debugging change the data-main for the require script tag in `index.html` 10 | to point at `assets/js/main.js`. (Look just before the closing `` tag!) 11 | 12 | * Once done, change it back to build for production using the `build.js` script in the `assets/js/lib` 13 | folder and [r.js](https://github.com/jrburke/r.js/). Then revert to `assets/js/main-built.js` 14 | 15 | * The full command is `r.js -o assets/js/lib/build.js` which should be run from the base directory. 16 | 17 | ### Adding new form elements 18 | 19 | * In the [js/data/yaml folder](https://github.com/minikomi/Bootstrap-Form-Builder/tree/gh-pages/assets/js/data/yaml) are yaml files, each of which corresponds to a tab in the form builder. 20 | * If you just want to add a new element you need to: 21 | - describe it in one of these files 22 | - parse the yaml to json using parse.rb in the same folder 23 | - create a corresponding template in the [templates/snippet directory](https://github.com/minikomi/Bootstrap-Form-Builder/tree/gh-pages/assets/js/templates/snippet) 24 | - add the template to [snippet-templates.js](https://github.com/minikomi/Bootstrap-Form-Builder/blob/gh-pages/assets/js/templates/snippet/snippet-templates.js) 25 | * If you want to add a new tab, you'll also need to adjust the [app.js file](https://github.com/minikomi/Bootstrap-Form-Builder/blob/gh-pages/assets/js/app.js) to make sure the tab is loaded. 26 | -------------------------------------------------------------------------------- /assets/js/views/temp-snippet.js: -------------------------------------------------------------------------------- 1 | define([ 2 | "jquery" 3 | , "views/snippet" 4 | , "text!templates/app/temp.html" 5 | , "helper/pubsub" 6 | ], function( 7 | $ 8 | , SnippetView 9 | , _tempTemplate 10 | , PubSub 11 | ){ 12 | return SnippetView.extend({ 13 | initialize: function(){ 14 | PubSub.on("newTempPostRender", this.postRender, this); 15 | this.constructor.__super__.initialize.call(this); 16 | this.tempTemplate = _.template(_tempTemplate); 17 | } 18 | , className: "temp" 19 | , render: function() { 20 | return this.$el.html(this.tempTemplate({text: this.constructor.__super__.render.call(this).html()})); 21 | } 22 | , postRender: function(mouseEvent){ 23 | this.tempForm = this.$el.find("form")[0]; 24 | this.halfHeight = Math.floor(this.tempForm.clientHeight/2); 25 | this.halfWidth = Math.floor(this.tempForm.clientWidth/2); 26 | this.centerOnEvent(mouseEvent); 27 | } 28 | , events:{ 29 | "mousemove": "mouseMoveHandler", 30 | "mouseup" : "mouseUpHandler" 31 | } 32 | , centerOnEvent: function(mouseEvent){ 33 | var mouseX = mouseEvent.pageX; 34 | var mouseY = mouseEvent.pageY; 35 | this.tempForm.style.top = (mouseY - this.halfHeight) + "px"; 36 | this.tempForm.style.left = (mouseX - this.halfWidth) + "px"; 37 | // Make sure the element has been drawn and 38 | // has height in the dom before triggering. 39 | PubSub.trigger("tempMove", mouseEvent); 40 | } 41 | , mouseMoveHandler: function(mouseEvent) { 42 | mouseEvent.preventDefault(); 43 | this.centerOnEvent(mouseEvent); 44 | } 45 | , mouseUpHandler: function(mouseEvent){ 46 | mouseEvent.preventDefault(); 47 | PubSub.trigger("tempDrop", mouseEvent, this.model); 48 | this.remove(); 49 | } 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | # Bootstrap v4 Form Builder 21 | 22 | [![Version](https://img.shields.io/npm/v/bootstrap-form-builder-v4.svg)](https://www.npmjs.com/package/bootstrap-form-builder-v4) [![Downloads](https://img.shields.io/npm/dt/bootstrap-form-builder-v4.svg)](https://www.npmjs.com/package/bootstrap-form-builder-v4) 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | > A drag-and-drop form builder for Bootstrap 4. 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | [![bootstrap-form-builder-v4](https://i.imgur.com/cFqF7nY.png)](https://bloggify.github.io/bootstrap-form-builder) 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | ## :question: Get Help 70 | 71 | There are few ways to get help: 72 | 73 | 74 | 75 | 1. Please [post questions on Stack Overflow](https://stackoverflow.com/questions/ask). You can open issues with questions, as long you add a link to your Stack Overflow question. 76 | 2. For bug reports and feature requests, open issues. :bug: 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | ## :yum: How to contribute 94 | Have an idea? Found a bug? See [how to contribute][contributing]. 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | ## :scroll: License 120 | 121 | [MIT][license] © [Bloggify][website] 122 | 123 | 124 | 125 | 126 | 127 | 128 | [license]: /LICENSE 129 | [website]: https://bloggify.org 130 | [contributing]: /CONTRIBUTING.md 131 | [docs]: /DOCUMENTATION.md 132 | -------------------------------------------------------------------------------- /assets/js/views/snippet.js: -------------------------------------------------------------------------------- 1 | define([ 2 | "jquery", "underscore", "backbone" 3 | , "text!templates/popover/popover-main.html" 4 | , "text!templates/popover/popover-input.html" 5 | , "text!templates/popover/popover-select.html" 6 | , "text!templates/popover/popover-textarea.html" 7 | , "text!templates/popover/popover-textarea-split.html" 8 | , "text!templates/popover/popover-checkbox.html" 9 | , "templates/snippet/snippet-templates" 10 | , "bootstrap" 11 | ], function( 12 | $, _, Backbone 13 | , _PopoverMain 14 | , _PopoverInput 15 | , _PopoverSelect 16 | , _PopoverTextArea 17 | , _PopoverTextAreaSplit 18 | , _PopoverCheckbox 19 | , _snippetTemplates 20 | ){ 21 | return Backbone.View.extend({ 22 | tagName: "div" 23 | , className: "component" 24 | , initialize: function(){ 25 | this.template = _.template(_snippetTemplates[this.model.idFriendlyTitle()]) 26 | this.popoverTemplates = { 27 | "input" : _.template(_PopoverInput) 28 | , "select" : _.template(_PopoverSelect) 29 | , "textarea" : _.template(_PopoverTextArea) 30 | , "textarea-split" : _.template(_PopoverTextAreaSplit) 31 | , "checkbox" : _.template(_PopoverCheckbox) 32 | } 33 | } 34 | , render: function(withAttributes){ 35 | var that = this; 36 | var content = _.template(_PopoverMain)({ 37 | "title": that.model.get("title"), 38 | "items" : that.model.get("fields"), 39 | "popoverTemplates": that.popoverTemplates 40 | }); 41 | if (withAttributes) { 42 | return this.$el.html( 43 | that.template(that.model.getValues()) 44 | ).attr({ 45 | "data-content" : content 46 | , "data-title" : that.model.get("title") 47 | , "data-trigger" : "manual" 48 | , "data-html" : true 49 | }); 50 | } else { 51 | return this.$el.html( 52 | that.template(that.model.getValues()) 53 | ) 54 | } 55 | } 56 | }); 57 | }); 58 | -------------------------------------------------------------------------------- /assets/js/data/yaml/radio.yaml: -------------------------------------------------------------------------------- 1 | - 2 | title : Multiple Radios 3 | fields : 4 | name: 5 | label : Group Name 6 | type : input 7 | value : radios 8 | label: 9 | label : Label Text 10 | type : input 11 | value : Multiple Radios 12 | radios: 13 | label : Radios 14 | type : textarea-split 15 | value : ["Option one", "Option two"] 16 | radiosValues: 17 | label : Radios Values 18 | type : textarea-split 19 | value : ["1", "2"] 20 | - 21 | title : Multiple Radios Inline 22 | fields : 23 | name: 24 | label : Group Name 25 | type : input 26 | value : radios 27 | label: 28 | label : Label Text 29 | type : input 30 | value : Inline Radios 31 | radios: 32 | label : Radios 33 | type : textarea-split 34 | value : [1,2,3,4] 35 | radiosValues: 36 | label : Radios Values 37 | type : textarea-split 38 | value : ["1", "2", "3", "4"] 39 | - title : Multiple Checkboxes 40 | fields : 41 | name: 42 | label : Group Name 43 | type : input 44 | value : checkboxes 45 | label: 46 | label : Label Text 47 | type : input 48 | value : Multiple Checkboxes 49 | checkboxes: 50 | label : Checkboxes 51 | type : textarea-split 52 | value : ["Option one", "Option two"] 53 | checkboxesValues: 54 | label : Checkboxes Values 55 | type : textarea-split 56 | value : ["1", "2"] 57 | - 58 | title : Multiple Checkboxes Inline 59 | fields : 60 | name: 61 | label : Group Name 62 | type : input 63 | value : checkboxes 64 | label: 65 | label : Label Text 66 | type : input 67 | value : Inline Checkboxes 68 | checkboxes: 69 | label : Checkboxes 70 | type : textarea-split 71 | value : [1,2,3,4] 72 | checkboxesValues: 73 | label : Checkboxes Values 74 | type : textarea-split 75 | value : ["1", "2", "3", "4"] 76 | 77 | 78 | -------------------------------------------------------------------------------- /assets/js/app.js: -------------------------------------------------------------------------------- 1 | define([ 2 | "jquery" , "underscore" , "backbone" 3 | , "collections/snippets" , "collections/my-form-snippets" 4 | , "views/tab" , "views/my-form" 5 | , "text!data/input.json", "text!data/radio.json", "text!data/select.json", "text!data/buttons.json" 6 | , "text!templates/app/render.html", "text!templates/app/about.html", 7 | ], function( 8 | $, _, Backbone 9 | , SnippetsCollection, MyFormSnippetsCollection 10 | , TabView, MyFormView 11 | , inputJSON, radioJSON, selectJSON, buttonsJSON 12 | , renderTab, aboutTab 13 | ){ 14 | return { 15 | initialize: function(){ 16 | 17 | //Bootstrap tabs from json. 18 | new TabView({ 19 | title: "Input" 20 | , collection: new SnippetsCollection(JSON.parse(inputJSON)) 21 | }); 22 | new TabView({ 23 | title: "Radios / Checkboxes" 24 | , collection: new SnippetsCollection(JSON.parse(radioJSON)) 25 | }); 26 | new TabView({ 27 | title: "Select" 28 | , collection: new SnippetsCollection(JSON.parse(selectJSON)) 29 | }); 30 | new TabView({ 31 | title: "Buttons" 32 | , collection: new SnippetsCollection(JSON.parse(buttonsJSON)) 33 | }); 34 | new TabView({ 35 | title: "Rendered" 36 | , content: renderTab 37 | }); 38 | new TabView({ 39 | title: "About" 40 | , content: aboutTab 41 | }); 42 | 43 | //Make the first tab active! 44 | $("#components .tab-pane").first().addClass("active"); 45 | $("#formtabs li a").first().addClass("active"); 46 | // Bootstrap "My Form" with 'Form Name' snippet. 47 | new MyFormView({ 48 | title: "Original" 49 | , collection: new MyFormSnippetsCollection([ 50 | { "title" : "Form Name" 51 | , "fields": { 52 | "name" : { 53 | "label" : "Form Name" 54 | , "type" : "input" 55 | , "value" : "Form Name" 56 | } 57 | } 58 | } 59 | ]) 60 | }); 61 | } 62 | } 63 | }); 64 | -------------------------------------------------------------------------------- /assets/css/custom.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 10px; /* 50px to make the container go all the way to the bottom of the topbar */ 3 | padding-bottom: 10px; 4 | position: relative; 5 | } 6 | 7 | @media (max-width: 979px) { 8 | body { 9 | padding-top: 0px; 10 | } 11 | } 12 | 13 | #components { 14 | min-height: 500px; 15 | margin-top:5px; 16 | } 17 | 18 | #target fieldset { 19 | border: 1px solid #ccc; 20 | min-height: 220px; 21 | padding: 5px; 22 | padding-bottom: 50px; 23 | } 24 | 25 | .component { 26 | cursor: pointer; 27 | } 28 | 29 | input { 30 | cursor: pointer; 31 | } 32 | 33 | textarea { 34 | resize: vertical; 35 | } 36 | .popover { 37 | z-index: 9999; 38 | } 39 | .popover .control-group { 40 | cursor: default; 41 | } 42 | .popover input { 43 | cursor: text; 44 | } 45 | 46 | .temp{ 47 | width: 100%; 48 | height: 100%; 49 | background: transparent; 50 | position: absolute; 51 | z-index: 999999; 52 | top: 0; 53 | overflow-x: hidden; 54 | left: 0; 55 | } 56 | 57 | .temp form { 58 | padding: 8px 4px; 59 | border: 1px dotted #ccc; 60 | position: absolute; 61 | background: white; 62 | box-shadow: 0px 0px 30px #999; 63 | -webkit-transition: -webkit-transform 0.1s ease-out; 64 | overflow: hidden; 65 | } 66 | 67 | #render{ 68 | min-height: 500px; 69 | font-family: monospace; 70 | } 71 | 72 | /* Popovers now are appended to their parent rather 73 | than to the body/main document as they once were. 74 | We need to over-ride some inherited stuff.. */ 75 | form .popover .controls { 76 | margin-left: 0; 77 | } 78 | 79 | form .popover .control-label { 80 | text-align: left; 81 | } 82 | 83 | form .popover form { 84 | width: 300px; 85 | } 86 | 87 | .popover-content form .btn{ 88 | /* margin-right: 10px */ 89 | } 90 | 91 | .target:after{ 92 | float:left; 93 | content: " "; 94 | height: 77px; 95 | margin-top: 3px; 96 | background: #f0f0f0; 97 | width: 100%; 98 | border: 1px dashed #ccc; 99 | } 100 | 101 | #target, #input, #radioscheckboxes, #select, #buttons { 102 | -webkit-touch-callout: none; 103 | -webkit-user-select: none; 104 | -khtml-user-select: none; 105 | -moz-user-select: none; 106 | -ms-user-select: none; 107 | user-select: none; 108 | } 109 | 110 | #target fieldset{ 111 | width: 100%; 112 | overflow: hidden; 113 | } 114 | 115 | label { font-weight: bold; } 116 | -------------------------------------------------------------------------------- /assets/js/data/yaml/select.yaml: -------------------------------------------------------------------------------- 1 | - 2 | title : Select Basic 3 | fields : 4 | id: 5 | label : ID / Name 6 | type : input 7 | value : selectbasic 8 | label: 9 | label : Label Text 10 | type : input 11 | value : Select Basic 12 | options: 13 | label : Options 14 | type : textarea-split 15 | value : ["Option one", "Option two"] 16 | values: 17 | label : Values 18 | type : textarea-split 19 | value : ["1", "2"] 20 | inputsize: 21 | label : Input Size 22 | type : select 23 | value : 24 | - 25 | value : input-mini 26 | label : Mini 27 | selected : false 28 | - 29 | value : input-small 30 | label : Small 31 | selected : false 32 | - 33 | value : input-medium 34 | label : Medium 35 | selected : false 36 | - 37 | value : input-large 38 | label : Large 39 | selected : false 40 | - 41 | value : input-xlarge 42 | label : Xlarge 43 | selected : true 44 | - 45 | value : input-xxlarge 46 | label : Xxlarge 47 | selected : false 48 | - 49 | title : Select Multiple 50 | fields : 51 | id: 52 | label : ID / Name 53 | type : input 54 | value : selectmultiple 55 | label: 56 | label : Label Text 57 | type : input 58 | value : Select Multiple 59 | options: 60 | label : Options 61 | type : textarea-split 62 | value : ["Option one", "Option two"] 63 | values: 64 | label : Values 65 | type : textarea-split 66 | value : ["1", "2"] 67 | inputsize: 68 | label : Input Size 69 | type : select 70 | value : 71 | - 72 | value : input-mini 73 | label : Mini 74 | selected : false 75 | - 76 | value : input-small 77 | label : Small 78 | selected : false 79 | - 80 | value : input-medium 81 | label : Medium 82 | selected : false 83 | - 84 | value : input-large 85 | label : Large 86 | selected : false 87 | - 88 | value : input-xlarge 89 | label : Xlarge 90 | selected : true 91 | - 92 | value : input-xxlarge 93 | label : Xxlarge 94 | selected : false 95 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # :eight_spoked_asterisk: :stars: :sparkles: :dizzy: :star2: :star2: :sparkles: :dizzy: :star2: :star2: Contributing :star: :star2: :dizzy: :sparkles: :star: :star2: :dizzy: :sparkles: :stars: :eight_spoked_asterisk: 2 | 3 | So, you want to contribute to this project! That's awesome. However, before 4 | doing so, please read the following simple steps how to contribute. This will 5 | make the life easier and will avoid wasting time on things which are not 6 | requested. :sparkles: 7 | 8 | ## Discuss the changes before doing them 9 | - First of all, open an issue in the repository, using the [bug tracker][1], 10 | describing the contribution you would like to make, the bug you found or any 11 | other ideas you have. This will help us to get you started on the right 12 | foot. 13 | 14 | - If it makes sense, add the platform and software information (e.g. operating 15 | system, Node.JS version etc.), screenshots (so we can see what you are 16 | seeing). 17 | 18 | - It is recommended to wait for feedback before continuing to next steps. 19 | However, if the issue is clear (e.g. a typo) and the fix is simple, you can 20 | continue and fix it. 21 | 22 | ## Fixing issues 23 | - Fork the project in your account and create a branch with your fix: 24 | `some-great-feature` or `some-issue-fix`. 25 | 26 | - Commit your changes in that branch, writing the code following the 27 | [code style][2]. If the project contains tests (generally, the `test` 28 | directory), you are encouraged to add a test as well. :memo: 29 | 30 | - If the project contains a `package.json` or a `bower.json` file add yourself 31 | in the `contributors` array (or `authors` in the case of `bower.json`; 32 | if the array does not exist, create it): 33 | 34 | ```json 35 | { 36 | "contributors": [ 37 | "Your Name (http://your.website)" 38 | ] 39 | } 40 | ``` 41 | 42 | ## Creating a pull request 43 | 44 | - Open a pull request, and reference the initial issue in the pull request 45 | message (e.g. *fixes #*). Write a good description and 46 | title, so everybody will know what is fixed/improved. 47 | 48 | - If it makes sense, add screenshots, gifs etc., so it is easier to see what 49 | is going on. 50 | 51 | ## Wait for feedback 52 | Before accepting your contributions, we will review them. You may get feedback 53 | about what should be fixed in your modified code. If so, just keep committing 54 | in your branch and the pull request will be updated automatically. 55 | 56 | ## Everyone is happy! 57 | Finally, your contributions will be merged, and everyone will be happy! :smile: 58 | Contributions are more than welcome! 59 | 60 | Thanks! :sweat_smile: 61 | 62 | 63 | 64 | [1]: https://github.com/Bloggify/bootstrap-form-builder/issues 65 | 66 | [2]: https://github.com/IonicaBizau/code-style 67 | -------------------------------------------------------------------------------- /assets/js/templates/snippet/snippet-templates.js: -------------------------------------------------------------------------------- 1 | define(function(require) { 2 | var formname = require('text!templates/snippet/formname.html') 3 | , prependedtext = require('text!templates/snippet/prependedtext.html') 4 | , search = require('text!templates/snippet/searchinput.html') 5 | , textinput = require('text!templates/snippet/textinput.html') 6 | , appendedcheckbox = require('text!templates/snippet/appendedcheckbox.html') 7 | , appendedtext = require('text!templates/snippet/appendedtext.html') 8 | , filebutton = require('text!templates/snippet/filebutton.html') 9 | , button = require('text!templates/snippet/button.html') 10 | , buttondouble = require('text!templates/snippet/buttondouble.html') 11 | , buttondropdown = require('text!templates/snippet/buttondropdown.html') 12 | , multiplecheckboxes = require('text!templates/snippet/multiplecheckboxes.html') 13 | , multiplecheckboxesinline = require('text!templates/snippet/multiplecheckboxesinline.html') 14 | , multipleradios = require('text!templates/snippet/multipleradios.html') 15 | , multipleradiosinline = require('text!templates/snippet/multipleradiosinline.html') 16 | , passwordinput = require('text!templates/snippet/passwordinput.html') 17 | , prependedcheckbox = require('text!templates/snippet/prependedcheckbox.html') 18 | , searchinput = require('text!templates/snippet/searchinput.html') 19 | , dateinput = require('text!templates/snippet/dateinput.html') 20 | , selectbasic = require('text!templates/snippet/selectbasic.html') 21 | , selectmultiple = require('text!templates/snippet/selectmultiple.html') 22 | , textarea = require('text!templates/snippet/textarea.html'); 23 | 24 | return { 25 | formname : formname 26 | , prependedtext : prependedtext 27 | , search : search 28 | , textinput : textinput 29 | , dateinput : dateinput 30 | , appendedcheckbox : appendedcheckbox 31 | , appendedtext : appendedtext 32 | , filebutton : filebutton 33 | , singlebutton : button 34 | , doublebutton : buttondouble 35 | , buttondropdown : buttondropdown 36 | , multiplecheckboxes : multiplecheckboxes 37 | , multiplecheckboxesinline : multiplecheckboxesinline 38 | , multipleradios : multipleradios 39 | , multipleradiosinline : multipleradiosinline 40 | , passwordinput : passwordinput 41 | , prependedcheckbox : prependedcheckbox 42 | , searchinput : searchinput 43 | , selectbasic : selectbasic 44 | , selectmultiple : selectmultiple 45 | , textarea : textarea 46 | } 47 | }); 48 | -------------------------------------------------------------------------------- /assets/js/data/radio.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "title": "Multiple Radios", 4 | "fields": { 5 | "name": { 6 | "label": "Group Name", 7 | "type": "input", 8 | "value": "radios" 9 | }, 10 | "label": { 11 | "label": "Label Text", 12 | "type": "input", 13 | "value": "Multiple Radios" 14 | }, 15 | "radios": { 16 | "label": "Radios", 17 | "type": "textarea-split", 18 | "value": [ 19 | "Option one", 20 | "Option two" 21 | ] 22 | }, 23 | "radiosValues" :{ 24 | "label" : "Radios Values", 25 | "type" : "textarea-split", 26 | "value" :[ 27 | "1", 28 | "2" 29 | ] 30 | } 31 | } 32 | }, 33 | { 34 | "title": "Multiple Radios Inline", 35 | "fields": { 36 | "name": { 37 | "label": "Group Name", 38 | "type": "input", 39 | "value": "radios" 40 | }, 41 | "label": { 42 | "label": "Label Text", 43 | "type": "input", 44 | "value": "Inline Radios" 45 | }, 46 | "radios": { 47 | "label": "Radios", 48 | "type": "textarea-split", 49 | "value": [ 50 | 1, 51 | 2, 52 | 3, 53 | 4 54 | ] 55 | }, 56 | "radiosValues" :{ 57 | "label" : "Radios Values", 58 | "type" : "textarea-split", 59 | "value" :[ 60 | "1", 61 | "2", 62 | "3", 63 | "4" 64 | ] 65 | } 66 | } 67 | }, 68 | { 69 | "title": "Multiple Checkboxes", 70 | "fields": { 71 | "name": { 72 | "label": "Group Name", 73 | "type": "input", 74 | "value": "checkboxes" 75 | }, 76 | "label": { 77 | "label": "Label Text", 78 | "type": "input", 79 | "value": "Multiple Checkboxes" 80 | }, 81 | "checkboxes": { 82 | "label": "Checkboxes", 83 | "type": "textarea-split", 84 | "value": [ 85 | "Option one", 86 | "Option two" 87 | ] 88 | }, 89 | "checkboxesValues":{ 90 | "label" : "Checkboxes Values", 91 | "type" : "textarea-split", 92 | "value" : [ 93 | "1", 94 | "2" 95 | ] 96 | } 97 | } 98 | }, 99 | { 100 | "title": "Multiple Checkboxes Inline", 101 | "fields": { 102 | "name": { 103 | "label": "Group Name", 104 | "type": "input", 105 | "value": "checkboxes" 106 | }, 107 | "label": { 108 | "label": "Label Text", 109 | "type": "input", 110 | "value": "Inline Checkboxes" 111 | }, 112 | "checkboxes": { 113 | "label": "Checkboxes", 114 | "type": "textarea-split", 115 | "value": [ 116 | 1, 117 | 2, 118 | 3, 119 | 4 120 | ] 121 | }, 122 | "checkboxesValues":{ 123 | "label" : "Checkboxes Values", 124 | "type" : "textarea-split", 125 | "value" : [ 126 | "1", 127 | "2", 128 | "3", 129 | "4" 130 | ] 131 | } 132 | } 133 | } 134 | ] -------------------------------------------------------------------------------- /assets/js/views/my-form.js: -------------------------------------------------------------------------------- 1 | define([ 2 | "jquery", "underscore", "backbone" 3 | , "views/temp-snippet" 4 | , "helper/pubsub" 5 | , "text!templates/app/renderform.html" , "Guid" 6 | ], function( 7 | $, _, Backbone 8 | , TempSnippetView 9 | , PubSub 10 | , _renderForm 11 | ){ 12 | return Backbone.View.extend({ 13 | tagName: "fieldset" 14 | , initialize: function(){ 15 | this.collection.on("add", this.render, this); 16 | this.collection.on("remove", this.render, this); 17 | this.collection.on("change", this.render, this); 18 | PubSub.on("mySnippetDrag", this.handleSnippetDrag, this); 19 | PubSub.on("tempMove", this.handleTempMove, this); 20 | PubSub.on("tempDrop", this.handleTempDrop, this); 21 | this.$build = $("#build"); 22 | this.build = document.getElementById("build"); 23 | this.buildBCR = this.build.getBoundingClientRect(); 24 | this.renderForm = _.template(_renderForm); 25 | this.render(); 26 | } 27 | 28 | , render: function(){ 29 | //Render Snippet Views 30 | this.$el.empty(); 31 | var that = this; 32 | _.each(this.collection.renderAll(), function(snippet){ 33 | 34 | that.$el.append(snippet); 35 | }); 36 | $("#render").val(that.renderForm({ 37 | text: _.map(this.collection.renderAllClean(), function(e){return e.html()}).join("\n") 38 | })); 39 | this.$el.appendTo("#build form"); 40 | this.delegateEvents(); 41 | } 42 | 43 | , getBottomAbove: function(eventY){ 44 | var myFormBits = $(this.$el.find(".component")); 45 | var topelement = _.find(myFormBits, function(renderedSnippet) { 46 | 47 | if (($(renderedSnippet).position().top + $(renderedSnippet).height()) > eventY - 160) { 48 | return true; 49 | } 50 | else { 51 | return false; 52 | } 53 | }); 54 | if (topelement){ 55 | return topelement; 56 | } else { 57 | return myFormBits[0]; 58 | } 59 | } 60 | 61 | , handleSnippetDrag: function(mouseEvent, snippetModel) { 62 | $("body").append(new TempSnippetView({model: snippetModel}).render()); 63 | this.collection.remove(snippetModel); 64 | PubSub.trigger("newTempPostRender", mouseEvent); 65 | } 66 | 67 | , handleTempMove: function(mouseEvent){ 68 | $(".target").removeClass("target"); 69 | if(mouseEvent.pageX >= this.buildBCR.left && 70 | mouseEvent.pageX < (this.$build.width() + this.buildBCR.left) && 71 | mouseEvent.pageY >= this.buildBCR.top && 72 | mouseEvent.pageY < (this.$build.height() + this.buildBCR.top)){ 73 | $(this.getBottomAbove(mouseEvent.pageY)).addClass("target"); 74 | } else { 75 | $(".target").removeClass("target"); 76 | } 77 | } 78 | 79 | , handleTempDrop: function(mouseEvent, model, index){ 80 | if(mouseEvent.pageX >= this.buildBCR.left && 81 | mouseEvent.pageX < (this.$build.width() + this.buildBCR.left) && 82 | mouseEvent.pageY >= this.buildBCR.top && 83 | mouseEvent.pageY < (this.$build.height() + this.buildBCR.top)) { 84 | var index = $(".target").index(); 85 | $(".target").removeClass("target"); 86 | this.collection.add(model,{at: index+1}); 87 | } else { 88 | $(".target").removeClass("target"); 89 | } 90 | } 91 | }) 92 | }); 93 | -------------------------------------------------------------------------------- /assets/js/data/select.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "title": "Select Basic", 4 | "fields": { 5 | "id": { 6 | "label": "ID / Name", 7 | "type": "input", 8 | "value": "selectbasic" 9 | }, 10 | "label": { 11 | "label": "Label Text", 12 | "type": "input", 13 | "value": "Select Basic" 14 | }, 15 | "options": { 16 | "label": "Options", 17 | "type": "textarea-split", 18 | "value": [ 19 | "Option one", 20 | "Option two" 21 | ] 22 | }, 23 | "values":{ 24 | "label": "Values", 25 | "type": "textarea-split", 26 | "value": [ 27 | "1", 28 | "2" 29 | ] 30 | }, 31 | "inputsize": { 32 | "label": "Input Size", 33 | "type": "select", 34 | "value": [ 35 | { 36 | "value": "col-md-1", 37 | "label": "Mini", 38 | "selected": false 39 | }, 40 | { 41 | "value": "col-md-2", 42 | "label": "Small", 43 | "selected": false 44 | }, 45 | { 46 | "value": "col-md-4", 47 | "label": "Medium", 48 | "selected": true 49 | }, 50 | { 51 | "value": "col-md-5", 52 | "label": "Large", 53 | "selected": false 54 | }, 55 | { 56 | "value": "col-md-6", 57 | "label": "Xlarge", 58 | "selected": false 59 | }, 60 | { 61 | "value": "col-md-8", 62 | "label": "Xxlarge", 63 | "selected": false 64 | } 65 | ] 66 | } 67 | } 68 | }, 69 | { 70 | "title": "Select Multiple", 71 | "fields": { 72 | "id": { 73 | "label": "ID / Name", 74 | "type": "input", 75 | "value": "selectmultiple" 76 | }, 77 | "label": { 78 | "label": "Label Text", 79 | "type": "input", 80 | "value": "Select Multiple" 81 | }, 82 | "options": { 83 | "label": "Options", 84 | "type": "textarea-split", 85 | "value": [ 86 | "Option one", 87 | "Option two" 88 | ] 89 | }, 90 | "values":{ 91 | "label": "Values", 92 | "type": "textarea-split", 93 | "value": [ 94 | "1", 95 | "2" 96 | ] 97 | }, 98 | "inputsize": { 99 | "label": "Input Size", 100 | "type": "select", 101 | "value": [ 102 | { 103 | "value": "col-md-1", 104 | "label": "Mini", 105 | "selected": false 106 | }, 107 | { 108 | "value": "col-md-2", 109 | "label": "Small", 110 | "selected": false 111 | }, 112 | { 113 | "value": "col-md-4", 114 | "label": "Medium", 115 | "selected": true 116 | }, 117 | { 118 | "value": "col-md-5", 119 | "label": "Large", 120 | "selected": false 121 | }, 122 | { 123 | "value": "col-md-6", 124 | "label": "Xlarge", 125 | "selected": false 126 | }, 127 | { 128 | "value": "col-md-8", 129 | "label": "Xxlarge", 130 | "selected": false 131 | } 132 | ] 133 | } 134 | } 135 | } 136 | ] -------------------------------------------------------------------------------- /assets/js/views/my-form-snippet.js: -------------------------------------------------------------------------------- 1 | define([ 2 | "jquery", "underscore", "backbone", 3 | "views/snippet", "views/temp-snippet", 4 | "helper/pubsub" 5 | ], function( 6 | $, _, Backbone, 7 | SnippetView, TempSnippetView, 8 | PubSub 9 | ){ 10 | return SnippetView.extend({ 11 | events:{ 12 | "click" : "preventPropagation" //stops checkbox / radio reacting. 13 | , "mousedown" : "mouseDownHandler" 14 | , "mouseup" : "mouseUpHandler" 15 | } 16 | 17 | , mouseDownHandler : function(mouseDownEvent){ 18 | mouseDownEvent.stopPropagation(); 19 | mouseDownEvent.preventDefault(); 20 | var that = this; 21 | //popover 22 | $(".popover").remove(); 23 | this.$el.popover({ placement: 'left' }).popover("show"); 24 | $(".popover #save").on("click", this.saveHandler(that)); 25 | $(".popover #cancel").on("click", this.cancelHandler(that)); 26 | //add drag event for all but form name 27 | if(this.model.get("title") !== "Form Name"){ 28 | $("body").on("mousemove", function(mouseMoveEvent){ 29 | if( 30 | Math.abs(mouseDownEvent.pageX - mouseMoveEvent.pageX) > 10 || 31 | Math.abs(mouseDownEvent.pageY - mouseMoveEvent.pageY) > 10 32 | ){ 33 | that.$el.popover('dispose'); 34 | PubSub.trigger("mySnippetDrag", mouseDownEvent, that.model); 35 | that.mouseUpHandler(); 36 | }; 37 | }); 38 | } 39 | } 40 | 41 | , preventPropagation: function(e) { 42 | e.stopPropagation(); 43 | e.preventDefault(); 44 | } 45 | 46 | , mouseUpHandler : function(mouseUpEvent) { 47 | $("body").off("mousemove"); 48 | } 49 | 50 | , saveHandler : function(boundContext) { 51 | return function(mouseEvent) { 52 | mouseEvent.preventDefault(); 53 | var fields = $(".popover .field"); 54 | _.each(fields, function(e){ 55 | var $e = $(e) 56 | , type = $e.attr("data-type") 57 | , name = $e.attr("id"); 58 | 59 | switch(type) { 60 | case "checkbox": 61 | boundContext.model.setField(name, $e.is(":checked")); 62 | break; 63 | case "input": 64 | boundContext.model.setField(name, $e.val()); 65 | break; 66 | case "textarea": 67 | boundContext.model.setField(name, $e.val()); 68 | break; 69 | case "textarea-split": 70 | boundContext.model.setField(name, 71 | _.chain($e.val().split("\n")) 72 | .map(function(t){return $.trim(t)}) 73 | .filter(function(t){return t.length > 0}) 74 | .value() 75 | ); 76 | break; 77 | case "select": 78 | var valarr = _.map($e.find("option"), function(e){ 79 | return {value: e.value, selected: e.selected, label:$(e).text()}; 80 | }); 81 | boundContext.model.setField(name, valarr); 82 | break; 83 | } 84 | }); 85 | boundContext.model.trigger("change"); 86 | $(".popover").remove(); 87 | } 88 | } 89 | 90 | , cancelHandler : function(boundContext) { 91 | return function(mouseEvent) { 92 | mouseEvent.preventDefault(); 93 | $(".popover").remove(); 94 | boundContext.model.trigger("change"); 95 | } 96 | } 97 | 98 | }); 99 | }); 100 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Bootstrap Form Builder 5 | 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 |
29 | 30 | 31 | 32 |
33 |
Drag & Drop components
34 |
35 |
36 | 39 |
40 |
41 |
42 | 43 |
44 |
45 |
46 |
47 |
48 | 49 | 50 | 51 | 52 |
53 |
54 |
Your Form
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | Coded with JavaScript and 💖 64 |
65 |
66 |
67 | 68 | 69 |
70 | 71 |
72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /assets/js/data/yaml/buttons.yaml: -------------------------------------------------------------------------------- 1 | - 2 | title : File Button 3 | fields : 4 | id: 5 | label : ID / Name 6 | type : input 7 | value : filebutton 8 | label: 9 | label : Label Text 10 | type : input 11 | value : File Button 12 | - 13 | title : Single Button 14 | fields : 15 | id: 16 | label : ID / Name 17 | type : input 18 | value : singlebutton 19 | label: 20 | label : Label Text 21 | type : input 22 | value : Single Button 23 | buttonlabel: 24 | label : Button Label 25 | type : input 26 | value : Button 27 | buttontype: 28 | label : Button Type 29 | type : select 30 | value : 31 | - 32 | value: btn-default 33 | label: Default 34 | selected: false 35 | - 36 | value: btn-primary 37 | label: Primary 38 | selected: true 39 | - 40 | value: btn-info 41 | label: Info 42 | selected: false 43 | - 44 | value: btn-success 45 | label: Success 46 | selected: false 47 | - 48 | value: btn-warning 49 | label: Warning 50 | selected: false 51 | - 52 | value: btn-danger 53 | label: Danger 54 | selected: false 55 | - 56 | value: btn-inverse 57 | label: Inverse 58 | selected: false 59 | - 60 | title : Double Button 61 | fields : 62 | id: 63 | label : Button 1 ID / Name 64 | type : input 65 | value : button1id 66 | label: 67 | label : Label Text 68 | type : input 69 | value : Double Button 70 | button1label: 71 | label : Button Label 72 | type : input 73 | value : Good Button 74 | button1type: 75 | label : Button Type 76 | type : select 77 | value : 78 | - 79 | value: btn-default 80 | label: Default 81 | selected: false 82 | - 83 | value: btn-primary 84 | label: Primary 85 | selected: false 86 | - 87 | value: btn-info 88 | label: Info 89 | selected: false 90 | - 91 | value: btn-success 92 | label: Success 93 | selected: true 94 | - 95 | value: btn-warning 96 | label: Warning 97 | selected: false 98 | - 99 | value: btn-danger 100 | label: Danger 101 | selected: false 102 | - 103 | value: btn-inverse 104 | label: Inverse 105 | selected: false 106 | id2: 107 | label : Button 2 ID / Name 108 | type : input 109 | value : button2id 110 | button2label: 111 | label : Button Label 112 | type : input 113 | value : Scary Button 114 | button2type: 115 | label : Button Type 116 | type : select 117 | value : 118 | - 119 | value: btn-default 120 | label: Default 121 | selected: false 122 | - 123 | value: btn-primary 124 | label: Primary 125 | selected: false 126 | - 127 | value: btn-info 128 | label: Info 129 | selected: false 130 | - 131 | value: btn-success 132 | label: Success 133 | selected: false 134 | - 135 | value: btn-warning 136 | label: Warning 137 | selected: false 138 | - 139 | value: btn-danger 140 | label: Danger 141 | selected: true 142 | - 143 | value: btn-inverse 144 | label: Inverse 145 | selected: false 146 | -------------------------------------------------------------------------------- /assets/js/data/buttons.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "title": "File Button", 4 | "fields": { 5 | "id": { 6 | "label": "ID / Name", 7 | "type": "input", 8 | "value": "filebutton" 9 | }, 10 | "label": { 11 | "label": "Label Text", 12 | "type": "input", 13 | "value": "File Button" 14 | } 15 | } 16 | }, 17 | { 18 | "title": "Single Button", 19 | "fields": { 20 | "id": { 21 | "label": "ID / Name", 22 | "type": "input", 23 | "value": "singlebutton" 24 | }, 25 | "label": { 26 | "label": "Label Text", 27 | "type": "input", 28 | "value": "Single Button" 29 | }, 30 | "buttonlabel": { 31 | "label": "Button Label", 32 | "type": "input", 33 | "value": "Button" 34 | }, 35 | "buttontype": { 36 | "label": "Button Type", 37 | "type": "select", 38 | "value": [ 39 | { 40 | "value": "btn-default", 41 | "label": "Default", 42 | "selected": false 43 | }, 44 | { 45 | "value": "btn-primary", 46 | "label": "Primary", 47 | "selected": true 48 | }, 49 | { 50 | "value": "btn-info", 51 | "label": "Info", 52 | "selected": false 53 | }, 54 | { 55 | "value": "btn-success", 56 | "label": "Success", 57 | "selected": false 58 | }, 59 | { 60 | "value": "btn-warning", 61 | "label": "Warning", 62 | "selected": false 63 | }, 64 | { 65 | "value": "btn-danger", 66 | "label": "Danger", 67 | "selected": false 68 | }, 69 | { 70 | "value": "btn-inverse", 71 | "label": "Inverse", 72 | "selected": false 73 | } 74 | ] 75 | } 76 | } 77 | }, 78 | { 79 | "title": "Double Button", 80 | "fields": { 81 | "id": { 82 | "label": "Button 1 ID / Name", 83 | "type": "input", 84 | "value": "button1id" 85 | }, 86 | "label": { 87 | "label": "Label Text", 88 | "type": "input", 89 | "value": "Double Button" 90 | }, 91 | "button1label": { 92 | "label": "Button Label", 93 | "type": "input", 94 | "value": "Good Button" 95 | }, 96 | "button1type": { 97 | "label": "Button Type", 98 | "type": "select", 99 | "value": [ 100 | { 101 | "value": "btn-default", 102 | "label": "Default", 103 | "selected": false 104 | }, 105 | { 106 | "value": "btn-primary", 107 | "label": "Primary", 108 | "selected": false 109 | }, 110 | { 111 | "value": "btn-info", 112 | "label": "Info", 113 | "selected": false 114 | }, 115 | { 116 | "value": "btn-success", 117 | "label": "Success", 118 | "selected": true 119 | }, 120 | { 121 | "value": "btn-warning", 122 | "label": "Warning", 123 | "selected": false 124 | }, 125 | { 126 | "value": "btn-danger", 127 | "label": "Danger", 128 | "selected": false 129 | }, 130 | { 131 | "value": "btn-inverse", 132 | "label": "Inverse", 133 | "selected": false 134 | } 135 | ] 136 | }, 137 | "id2": { 138 | "label": "Button 2 ID / Name", 139 | "type": "input", 140 | "value": "button2id" 141 | }, 142 | "button2label": { 143 | "label": "Button Label", 144 | "type": "input", 145 | "value": "Scary Button" 146 | }, 147 | "button2type": { 148 | "label": "Button Type", 149 | "type": "select", 150 | "value": [ 151 | { 152 | "value": "btn-default", 153 | "label": "Default", 154 | "selected": false 155 | }, 156 | { 157 | "value": "btn-primary", 158 | "label": "Primary", 159 | "selected": false 160 | }, 161 | { 162 | "value": "btn-info", 163 | "label": "Info", 164 | "selected": false 165 | }, 166 | { 167 | "value": "btn-success", 168 | "label": "Success", 169 | "selected": false 170 | }, 171 | { 172 | "value": "btn-warning", 173 | "label": "Warning", 174 | "selected": false 175 | }, 176 | { 177 | "value": "btn-danger", 178 | "label": "Danger", 179 | "selected": true 180 | }, 181 | { 182 | "value": "btn-inverse", 183 | "label": "Inverse", 184 | "selected": false 185 | } 186 | ] 187 | } 188 | } 189 | } 190 | ] -------------------------------------------------------------------------------- /assets/js/data/yaml/input.yaml: -------------------------------------------------------------------------------- 1 | - 2 | title : Text Input 3 | fields : 4 | id: 5 | label : ID / Name 6 | type : input 7 | value : textinput 8 | label: 9 | label : Label Text 10 | type : input 11 | value : Text Input 12 | placeholder: 13 | label : Placeholder 14 | type : input 15 | value : placeholder 16 | helptext: 17 | label : Help Text 18 | type : input 19 | value : help 20 | required: 21 | label : Required 22 | type : checkbox 23 | value : false 24 | inputsize: 25 | label : Input Size 26 | type : select 27 | value : 28 | - 29 | value : input-mini 30 | label : Mini 31 | selected : false 32 | - 33 | value : input-small 34 | label : Small 35 | selected : false 36 | - 37 | value : input-medium 38 | label : Medium 39 | selected : false 40 | - 41 | value : input-large 42 | label : Large 43 | selected : false 44 | - 45 | value : input-xlarge 46 | label : Xlarge 47 | selected : true 48 | - 49 | value : input-xxlarge 50 | label : Xxlarge 51 | selected : false 52 | - 53 | title : Password Input 54 | fields : 55 | id: 56 | label : ID / Name 57 | type : input 58 | value : passwordinput 59 | label: 60 | label : Label Text 61 | type : input 62 | value : Password Input 63 | placeholder: 64 | label : Placeholder 65 | type : input 66 | value : placeholder 67 | helptext: 68 | label : Help Text 69 | type : input 70 | value : help 71 | required: 72 | label : Required 73 | type : checkbox 74 | value : false 75 | inputsize: 76 | label : Input Size 77 | type : select 78 | value : 79 | - 80 | value : input-mini 81 | label : Mini 82 | selected : false 83 | - 84 | value : input-small 85 | label : Small 86 | selected : false 87 | - 88 | value : input-medium 89 | label : Medium 90 | selected : false 91 | - 92 | value : input-large 93 | label : Large 94 | selected : false 95 | - 96 | value : input-xlarge 97 | label : Xlarge 98 | selected : true 99 | - 100 | value : input-xxlarge 101 | label : Xxlarge 102 | selected : false 103 | - 104 | title : Search Input 105 | fields : 106 | id: 107 | label : ID / Name 108 | type : input 109 | value : searchinput 110 | label: 111 | label : Label Text 112 | type : input 113 | value : Search Input 114 | placeholder: 115 | label : Placeholder 116 | type : input 117 | value : placeholder 118 | helptext: 119 | label : Help Text 120 | type : input 121 | value : help 122 | required: 123 | label : Required 124 | type : checkbox 125 | value : false 126 | inputsize: 127 | label : Input Size 128 | type : select 129 | value : 130 | - 131 | value : input-mini 132 | label : Mini 133 | selected : false 134 | - 135 | value : input-small 136 | label : Small 137 | selected : false 138 | - 139 | value : input-medium 140 | label : Medium 141 | selected : false 142 | - 143 | value : input-large 144 | label : Large 145 | selected : false 146 | - 147 | value : input-xlarge 148 | label : Xlarge 149 | selected : true 150 | - 151 | value : input-xxlarge 152 | label : Xxlarge 153 | selected : false 154 | - 155 | title : Prepended Text 156 | fields : 157 | id: 158 | label : ID / Name 159 | type : input 160 | value : prependedtext 161 | label: 162 | label : Label Text 163 | type : input 164 | value : Prepended Text 165 | prepend: 166 | label : Prepend 167 | type : input 168 | value : prepend 169 | placeholder: 170 | label : Placeholder 171 | type : input 172 | value : placeholder 173 | helptext: 174 | label : Help Text 175 | type : input 176 | value : help 177 | required: 178 | label : Required 179 | type : checkbox 180 | value : false 181 | inputsize: 182 | label: Input Size 183 | type: select 184 | value: 185 | - 186 | value : input-mini 187 | label : Mini 188 | selected : false 189 | - 190 | value : input-small 191 | label : Small 192 | selected : false 193 | - 194 | value : input-medium 195 | label : Medium 196 | selected : false 197 | - 198 | value : input-large 199 | label : Large 200 | selected : false 201 | - 202 | value : input-xlarge 203 | label : Xlarge 204 | selected : true 205 | - 206 | value : input-xxlarge 207 | label : Xxlarge 208 | selected : false 209 | - 210 | title : Appended Text 211 | fields : 212 | id: 213 | label : ID / Name 214 | type : input 215 | value : appendedtext 216 | label: 217 | label : Label Text 218 | type : input 219 | value : Appended Text 220 | append: 221 | label : Append 222 | type : input 223 | value : append 224 | placeholder: 225 | label : Placeholder 226 | type : input 227 | value : placeholder 228 | helptext: 229 | label : Help Text 230 | type : input 231 | value : help 232 | required: 233 | label : Required 234 | type : checkbox 235 | value : false 236 | inputsize: 237 | label: Input Size 238 | type: select 239 | value: 240 | - 241 | value : input-mini 242 | label : Mini 243 | selected : false 244 | - 245 | value : input-small 246 | label : Small 247 | selected : false 248 | - 249 | value : input-medium 250 | label : Medium 251 | selected : false 252 | - 253 | value : input-large 254 | label : Large 255 | selected : false 256 | - 257 | value : input-xlarge 258 | label : Xlarge 259 | selected : true 260 | - 261 | value : input-xxlarge 262 | label : Xxlarge 263 | selected : false 264 | - 265 | title : Prepended Checkbox 266 | fields : 267 | id: 268 | label : ID / Name 269 | type : input 270 | value : prependedcheckbox 271 | label: 272 | label : Label Text 273 | type : input 274 | value : Prepended Checkbox 275 | placeholder: 276 | label : Placeholder 277 | type : input 278 | value : placeholder 279 | helptext: 280 | label : Help Text 281 | type : input 282 | value : help 283 | checked: 284 | label : Checked 285 | type : checkbox 286 | value : false 287 | required: 288 | label : Required 289 | type : checkbox 290 | value : false 291 | inputsize: 292 | label: Input Size 293 | type: select 294 | value: 295 | - 296 | value : input-mini 297 | label : Mini 298 | selected : false 299 | - 300 | value : input-small 301 | label : Small 302 | selected : false 303 | - 304 | value : input-medium 305 | label : Medium 306 | selected : false 307 | - 308 | value : input-large 309 | label : Large 310 | selected : false 311 | - 312 | value : input-xlarge 313 | label : Xlarge 314 | selected : true 315 | - 316 | value : input-xxlarge 317 | label : Xxlarge 318 | selected : false 319 | - 320 | title : Appended Checkbox 321 | fields : 322 | id: 323 | label : ID / Name 324 | type : input 325 | value : appendedcheckbox 326 | label: 327 | label : Label Text 328 | type : input 329 | value : Appended Checkbox 330 | placeholder: 331 | label : Placeholder 332 | type : input 333 | value : placeholder 334 | helptext: 335 | label : Help Text 336 | type : input 337 | value : help 338 | checked: 339 | label : Checked 340 | type : checkbox 341 | value : false 342 | required: 343 | label : Required 344 | type : checkbox 345 | value : false 346 | inputsize: 347 | label: Input Size 348 | type: select 349 | value: 350 | - 351 | value : input-mini 352 | label : Mini 353 | selected : false 354 | - 355 | value : input-small 356 | label : Small 357 | selected : false 358 | - 359 | value : input-medium 360 | label : Medium 361 | selected : false 362 | - 363 | value : input-large 364 | label : Large 365 | selected : false 366 | - 367 | value : input-xlarge 368 | label : Xlarge 369 | selected : true 370 | - 371 | value : input-xxlarge 372 | label : Xxlarge 373 | selected : false 374 | - 375 | title: Button Drop Down 376 | fields: 377 | id: 378 | label : ID / Name 379 | type : input 380 | value : buttondropdown 381 | label: 382 | label: Label Text 383 | type: input 384 | value: Button Drop Down 385 | placeholder: 386 | label : Placeholder 387 | type : input 388 | value : placeholder 389 | buttontext: 390 | label : Button Text 391 | type : input 392 | value : Action 393 | buttonoptions: 394 | label : Options 395 | type : textarea-split 396 | value : ["Option one", "Option two", "Option three"] 397 | required: 398 | label : Required 399 | type : checkbox 400 | value : false 401 | inputsize: 402 | label : Input Size 403 | type : select 404 | value : 405 | - 406 | value: input-mini 407 | label : Mini 408 | selected : false 409 | - 410 | value : input-small 411 | label : Small 412 | selected : false 413 | - 414 | value : input-medium 415 | label : Medium 416 | selected : false 417 | - 418 | value : input-large 419 | label : Large 420 | selected : false 421 | - 422 | value : input-xlarge 423 | label : Xlarge 424 | selected : true 425 | - 426 | value : input-xxlarge 427 | label : Xxlarge 428 | selected : false 429 | - 430 | title : Text Area 431 | fields : 432 | id: 433 | label : ID / Name 434 | type : input 435 | value : textarea 436 | label: 437 | label : Label Text 438 | type : input 439 | value : Text Area 440 | textarea: 441 | label : Starting Text 442 | type : textarea 443 | value : default text 444 | -------------------------------------------------------------------------------- /assets/js/lib/require.js: -------------------------------------------------------------------------------- 1 | /* 2 | RequireJS 2.1.1 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved. 3 | Available via the MIT or new BSD license. 4 | see: http://github.com/jrburke/requirejs for details 5 | */ 6 | var requirejs,require,define; 7 | (function(W){function D(b){return M.call(b)==="[object Function]"}function E(b){return M.call(b)==="[object Array]"}function t(b,c){if(b){var d;for(d=0;d-1;d-=1)if(b[d]&&c(b[d],d,b))break}}function A(b,c){for(var d in b)if(b.hasOwnProperty(d)&&c(b[d],d))break}function O(b,c,d,g){c&&A(c,function(c,j){if(d||!F.call(b,j))g&&typeof c!=="string"?(b[j]||(b[j]={}),O(b[j],c,d,g)):b[j]=c});return b}function r(b,c){return function(){return c.apply(b, 8 | arguments)}}function X(b){if(!b)return b;var c=W;t(b.split("."),function(b){c=c[b]});return c}function G(b,c,d,g){c=Error(c+"\nhttp://requirejs.org/docs/errors.html#"+b);c.requireType=b;c.requireModules=g;if(d)c.originalError=d;return c}function ba(){if(H&&H.readyState==="interactive")return H;N(document.getElementsByTagName("script"),function(b){if(b.readyState==="interactive")return H=b});return H}var g,s,u,y,q,B,H,I,Y,Z,ca=/(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg,da=/[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g, 9 | $=/\.js$/,ea=/^\.\//;s=Object.prototype;var M=s.toString,F=s.hasOwnProperty,fa=Array.prototype.splice,v=!!(typeof window!=="undefined"&&navigator&&document),aa=!v&&typeof importScripts!=="undefined",ga=v&&navigator.platform==="PLAYSTATION 3"?/^complete$/:/^(complete|loaded)$/,R=typeof opera!=="undefined"&&opera.toString()==="[object Opera]",w={},n={},P=[],J=!1;if(typeof define==="undefined"){if(typeof requirejs!=="undefined"){if(D(requirejs))return;n=requirejs;requirejs=void 0}typeof require!=="undefined"&& 10 | !D(require)&&(n=require,require=void 0);g=requirejs=function(b,c,d,p){var i,j="_";!E(b)&&typeof b!=="string"&&(i=b,E(c)?(b=c,c=d,d=p):b=[]);if(i&&i.context)j=i.context;(p=w[j])||(p=w[j]=g.s.newContext(j));i&&p.configure(i);return p.require(b,c,d)};g.config=function(b){return g(b)};g.nextTick=typeof setTimeout!=="undefined"?function(b){setTimeout(b,4)}:function(b){b()};require||(require=g);g.version="2.1.1";g.jsExtRegExp=/^\/|:|\?|\.js$/;g.isBrowser=v;s=g.s={contexts:w,newContext:function(b){function c(a, 11 | f,x){var e,m,b,c,d,h,i,g=f&&f.split("/");e=g;var j=k.map,l=j&&j["*"];if(a&&a.charAt(0)===".")if(f){e=k.pkgs[f]?g=[f]:g.slice(0,g.length-1);f=a=e.concat(a.split("/"));for(e=0;f[e];e+=1)if(m=f[e],m===".")f.splice(e,1),e-=1;else if(m==="..")if(e===1&&(f[2]===".."||f[0]===".."))break;else e>0&&(f.splice(e-1,2),e-=2);e=k.pkgs[f=a[0]];a=a.join("/");e&&a===f+"/"+e.main&&(a=f)}else a.indexOf("./")===0&&(a=a.substring(2));if(x&&(g||l)&&j){f=a.split("/");for(e=f.length;e>0;e-=1){b=f.slice(0,e).join("/");if(g)for(m= 12 | g.length;m>0;m-=1)if(x=j[g.slice(0,m).join("/")])if(x=x[b]){c=x;d=e;break}if(c)break;!h&&l&&l[b]&&(h=l[b],i=e)}!c&&h&&(c=h,d=i);c&&(f.splice(0,d,c),a=f.join("/"))}return a}function d(a){v&&t(document.getElementsByTagName("script"),function(f){if(f.getAttribute("data-requiremodule")===a&&f.getAttribute("data-requirecontext")===h.contextName)return f.parentNode.removeChild(f),!0})}function p(a){var f=k.paths[a];if(f&&E(f)&&f.length>1)return d(a),f.shift(),h.require.undef(a),h.require([a]),!0}function i(a){var f, 13 | b=a?a.indexOf("!"):-1;b>-1&&(f=a.substring(0,b),a=a.substring(b+1,a.length));return[f,a]}function j(a,f,b,e){var m,K,d=null,g=f?f.name:null,j=a,l=!0,k="";a||(l=!1,a="_@r"+(M+=1));a=i(a);d=a[0];a=a[1];d&&(d=c(d,g,e),K=o[d]);a&&(d?k=K&&K.normalize?K.normalize(a,function(a){return c(a,g,e)}):c(a,g,e):(k=c(a,g,e),a=i(k),d=a[0],k=a[1],b=!0,m=h.nameToUrl(k)));b=d&&!K&&!b?"_unnormalized"+(N+=1):"";return{prefix:d,name:k,parentMap:f,unnormalized:!!b,url:m,originalName:j,isDefine:l,id:(d?d+"!"+k:k)+b}}function n(a){var f= 14 | a.id,b=l[f];b||(b=l[f]=new h.Module(a));return b}function q(a,f,b){var e=a.id,m=l[e];if(F.call(o,e)&&(!m||m.defineEmitComplete))f==="defined"&&b(o[e]);else n(a).on(f,b)}function z(a,f){var b=a.requireModules,e=!1;if(f)f(a);else if(t(b,function(f){if(f=l[f])f.error=a,f.events.error&&(e=!0,f.emit("error",a))}),!e)g.onError(a)}function s(){P.length&&(fa.apply(C,[C.length-1,0].concat(P)),P=[])}function u(a,f,b){var e=a.map.id;a.error?a.emit("error",a.error):(f[e]=!0,t(a.depMaps,function(e,c){var d=e.id, 15 | g=l[d];g&&!a.depMatched[c]&&!b[d]&&(f[d]?(a.defineDep(c,o[d]),a.check()):u(g,f,b))}),b[e]=!0)}function w(){var a,f,b,e,m=(b=k.waitSeconds*1E3)&&h.startTime+b<(new Date).getTime(),c=[],g=[],i=!1,j=!0;if(!S){S=!0;A(l,function(b){a=b.map;f=a.id;if(b.enabled&&(a.isDefine||g.push(b),!b.error))if(!b.inited&&m)p(f)?i=e=!0:(c.push(f),d(f));else if(!b.inited&&b.fetched&&a.isDefine&&(i=!0,!a.prefix))return j=!1});if(m&&c.length)return b=G("timeout","Load timeout for modules: "+c,null,c),b.contextName=h.contextName, 16 | z(b);j&&t(g,function(a){u(a,{},{})});if((!m||e)&&i)if((v||aa)&&!T)T=setTimeout(function(){T=0;w()},50);S=!1}}function y(a){n(j(a[0],null,!0)).init(a[1],a[2])}function B(a){var a=a.currentTarget||a.srcElement,b=h.onScriptLoad;a.detachEvent&&!R?a.detachEvent("onreadystatechange",b):a.removeEventListener("load",b,!1);b=h.onScriptError;a.detachEvent&&!R||a.removeEventListener("error",b,!1);return{node:a,id:a&&a.getAttribute("data-requiremodule")}}function I(){var a;for(s();C.length;)if(a=C.shift(),a[0]=== 17 | null)return z(G("mismatch","Mismatched anonymous define() module: "+a[a.length-1]));else y(a)}var S,U,h,L,T,k={waitSeconds:7,baseUrl:"./",paths:{},pkgs:{},shim:{},map:{},config:{}},l={},V={},C=[],o={},Q={},M=1,N=1;L={require:function(a){return a.require?a.require:a.require=h.makeRequire(a.map)},exports:function(a){a.usingExports=!0;if(a.map.isDefine)return a.exports?a.exports:a.exports=o[a.map.id]={}},module:function(a){return a.module?a.module:a.module={id:a.map.id,uri:a.map.url,config:function(){return k.config&& 18 | k.config[a.map.id]||{}},exports:o[a.map.id]}}};U=function(a){this.events=V[a.id]||{};this.map=a;this.shim=k.shim[a.id];this.depExports=[];this.depMaps=[];this.depMatched=[];this.pluginMaps={};this.depCount=0};U.prototype={init:function(a,b,c,e){e=e||{};if(!this.inited){this.factory=b;if(c)this.on("error",c);else this.events.error&&(c=r(this,function(a){this.emit("error",a)}));this.depMaps=a&&a.slice(0);this.errback=c;this.inited=!0;this.ignore=e.ignore;e.enabled||this.enabled?this.enable():this.check()}}, 19 | defineDep:function(a,b){this.depMatched[a]||(this.depMatched[a]=!0,this.depCount-=1,this.depExports[a]=b)},fetch:function(){if(!this.fetched){this.fetched=!0;h.startTime=(new Date).getTime();var a=this.map;if(this.shim)h.makeRequire(this.map,{enableBuildCallback:!0})(this.shim.deps||[],r(this,function(){return a.prefix?this.callPlugin():this.load()}));else return a.prefix?this.callPlugin():this.load()}},load:function(){var a=this.map.url;Q[a]||(Q[a]=!0,h.load(this.map.id,a))},check:function(){if(this.enabled&& 20 | !this.enabling){var a,b,c=this.map.id;b=this.depExports;var e=this.exports,m=this.factory;if(this.inited)if(this.error)this.emit("error",this.error);else{if(!this.defining){this.defining=!0;if(this.depCount<1&&!this.defined){if(D(m)){if(this.events.error)try{e=h.execCb(c,m,b,e)}catch(d){a=d}else e=h.execCb(c,m,b,e);if(this.map.isDefine)if((b=this.module)&&b.exports!==void 0&&b.exports!==this.exports)e=b.exports;else if(e===void 0&&this.usingExports)e=this.exports;if(a)return a.requireMap=this.map, 21 | a.requireModules=[this.map.id],a.requireType="define",z(this.error=a)}else e=m;this.exports=e;if(this.map.isDefine&&!this.ignore&&(o[c]=e,g.onResourceLoad))g.onResourceLoad(h,this.map,this.depMaps);delete l[c];this.defined=!0}this.defining=!1;if(this.defined&&!this.defineEmitted)this.defineEmitted=!0,this.emit("defined",this.exports),this.defineEmitComplete=!0}}else this.fetch()}},callPlugin:function(){var a=this.map,b=a.id,d=j(a.prefix);this.depMaps.push(d);q(d,"defined",r(this,function(e){var m, 22 | d;d=this.map.name;var x=this.map.parentMap?this.map.parentMap.name:null,i=h.makeRequire(a.parentMap,{enableBuildCallback:!0,skipMap:!0});if(this.map.unnormalized){if(e.normalize&&(d=e.normalize(d,function(a){return c(a,x,!0)})||""),e=j(a.prefix+"!"+d,this.map.parentMap),q(e,"defined",r(this,function(a){this.init([],function(){return a},null,{enabled:!0,ignore:!0})})),d=l[e.id]){this.depMaps.push(e);if(this.events.error)d.on("error",r(this,function(a){this.emit("error",a)}));d.enable()}}else m=r(this, 23 | function(a){this.init([],function(){return a},null,{enabled:!0})}),m.error=r(this,function(a){this.inited=!0;this.error=a;a.requireModules=[b];A(l,function(a){a.map.id.indexOf(b+"_unnormalized")===0&&delete l[a.map.id]});z(a)}),m.fromText=r(this,function(b,e){var f=a.name,c=j(f),d=J;e&&(b=e);d&&(J=!1);n(c);try{g.exec(b)}catch(x){throw Error("fromText eval for "+f+" failed: "+x);}d&&(J=!0);this.depMaps.push(c);h.completeLoad(f);i([f],m)}),e.load(a.name,i,m,k)}));h.enable(d,this);this.pluginMaps[d.id]= 24 | d},enable:function(){this.enabling=this.enabled=!0;t(this.depMaps,r(this,function(a,b){var c,e;if(typeof a==="string"){a=j(a,this.map.isDefine?this.map:this.map.parentMap,!1,!this.skipMap);this.depMaps[b]=a;if(c=L[a.id]){this.depExports[b]=c(this);return}this.depCount+=1;q(a,"defined",r(this,function(a){this.defineDep(b,a);this.check()}));this.errback&&q(a,"error",this.errback)}c=a.id;e=l[c];!L[c]&&e&&!e.enabled&&h.enable(a,this)}));A(this.pluginMaps,r(this,function(a){var b=l[a.id];b&&!b.enabled&& 25 | h.enable(a,this)}));this.enabling=!1;this.check()},on:function(a,b){var c=this.events[a];c||(c=this.events[a]=[]);c.push(b)},emit:function(a,b){t(this.events[a],function(a){a(b)});a==="error"&&delete this.events[a]}};h={config:k,contextName:b,registry:l,defined:o,urlFetched:Q,defQueue:C,Module:U,makeModuleMap:j,nextTick:g.nextTick,configure:function(a){a.baseUrl&&a.baseUrl.charAt(a.baseUrl.length-1)!=="/"&&(a.baseUrl+="/");var b=k.pkgs,c=k.shim,e={paths:!0,config:!0,map:!0};A(a,function(a,b){e[b]? 26 | b==="map"?O(k[b],a,!0,!0):O(k[b],a,!0):k[b]=a});if(a.shim)A(a.shim,function(a,b){E(a)&&(a={deps:a});if(a.exports&&!a.exportsFn)a.exportsFn=h.makeShimExports(a);c[b]=a}),k.shim=c;if(a.packages)t(a.packages,function(a){a=typeof a==="string"?{name:a}:a;b[a.name]={name:a.name,location:a.location||a.name,main:(a.main||"main").replace(ea,"").replace($,"")}}),k.pkgs=b;A(l,function(a,b){if(!a.inited&&!a.map.unnormalized)a.map=j(b)});if(a.deps||a.callback)h.require(a.deps||[],a.callback)},makeShimExports:function(a){return function(){var b; 27 | a.init&&(b=a.init.apply(W,arguments));return b||X(a.exports)}},makeRequire:function(a,f){function d(e,c,i){var k,p;if(f.enableBuildCallback&&c&&D(c))c.__requireJsBuild=!0;if(typeof e==="string"){if(D(c))return z(G("requireargs","Invalid require call"),i);if(a&&L[e])return L[e](l[a.id]);if(g.get)return g.get(h,e,a);k=j(e,a,!1,!0);k=k.id;return!F.call(o,k)?z(G("notloaded",'Module name "'+k+'" has not been loaded yet for context: '+b+(a?"":". Use require([])"))):o[k]}I();h.nextTick(function(){I();p= 28 | n(j(null,a));p.skipMap=f.skipMap;p.init(e,c,i,{enabled:!0});w()});return d}f=f||{};O(d,{isBrowser:v,toUrl:function(b){var d=b.lastIndexOf("."),f=null;d!==-1&&(f=b.substring(d,b.length),b=b.substring(0,d));return h.nameToUrl(c(b,a&&a.id,!0),f)},defined:function(b){b=j(b,a,!1,!0).id;return F.call(o,b)},specified:function(b){b=j(b,a,!1,!0).id;return F.call(o,b)||F.call(l,b)}});if(!a)d.undef=function(b){s();var c=j(b,a,!0),d=l[b];delete o[b];delete Q[c.url];delete V[b];if(d){if(d.events.defined)V[b]= 29 | d.events;delete l[b]}};return d},enable:function(a){l[a.id]&&n(a).enable()},completeLoad:function(a){var b,c,d=k.shim[a]||{},g=d.exports;for(s();C.length;){c=C.shift();if(c[0]===null){c[0]=a;if(b)break;b=!0}else c[0]===a&&(b=!0);y(c)}c=l[a];if(!b&&!o[a]&&c&&!c.inited)if(k.enforceDefine&&(!g||!X(g)))if(p(a))return;else return z(G("nodefine","No define call for "+a,null,[a]));else y([a,d.deps||[],d.exportsFn]);w()},nameToUrl:function(a,b){var c,d,i,h,j,l;if(g.jsExtRegExp.test(a))h=a+(b||"");else{c= 30 | k.paths;d=k.pkgs;h=a.split("/");for(j=h.length;j>0;j-=1)if(l=h.slice(0,j).join("/"),i=d[l],l=c[l]){E(l)&&(l=l[0]);h.splice(0,j,l);break}else if(i){c=a===i.name?i.location+"/"+i.main:i.location;h.splice(0,j,c);break}h=h.join("/");h+=b||(/\?/.test(h)?"":".js");h=(h.charAt(0)==="/"||h.match(/^[\w\+\.\-]+:/)?"":k.baseUrl)+h}return k.urlArgs?h+((h.indexOf("?")===-1?"?":"&")+k.urlArgs):h},load:function(a,b){g.load(h,a,b)},execCb:function(a,b,c,d){return b.apply(d,c)},onScriptLoad:function(a){if(a.type=== 31 | "load"||ga.test((a.currentTarget||a.srcElement).readyState))H=null,a=B(a),h.completeLoad(a.id)},onScriptError:function(a){var b=B(a);if(!p(b.id))return z(G("scripterror","Script error",a,[b.id]))}};h.require=h.makeRequire();return h}};g({});t(["toUrl","undef","defined","specified"],function(b){g[b]=function(){var c=w._;return c.require[b].apply(c,arguments)}});if(v&&(u=s.head=document.getElementsByTagName("head")[0],y=document.getElementsByTagName("base")[0]))u=s.head=y.parentNode;g.onError=function(b){throw b; 32 | };g.load=function(b,c,d){var g=b&&b.config||{},i;if(v)return i=g.xhtml?document.createElementNS("http://www.w3.org/1999/xhtml","html:script"):document.createElement("script"),i.type=g.scriptType||"text/javascript",i.charset="utf-8",i.async=!0,i.setAttribute("data-requirecontext",b.contextName),i.setAttribute("data-requiremodule",c),i.attachEvent&&!(i.attachEvent.toString&&i.attachEvent.toString().indexOf("[native code")<0)&&!R?(J=!0,i.attachEvent("onreadystatechange",b.onScriptLoad)):(i.addEventListener("load", 33 | b.onScriptLoad,!1),i.addEventListener("error",b.onScriptError,!1)),i.src=d,I=i,y?u.insertBefore(i,y):u.appendChild(i),I=null,i;else aa&&(importScripts(d),b.completeLoad(c))};v&&N(document.getElementsByTagName("script"),function(b){if(!u)u=b.parentNode;if(q=b.getAttribute("data-main")){if(!n.baseUrl)B=q.split("/"),Y=B.pop(),Z=B.length?B.join("/")+"/":"./",n.baseUrl=Z,q=Y;q=q.replace($,"");n.deps=n.deps?n.deps.concat(q):[q];return!0}});define=function(b,c,d){var g,i;typeof b!=="string"&&(d=c,c=b,b= 34 | null);E(c)||(d=c,c=[]);!c.length&&D(d)&&d.length&&(d.toString().replace(ca,"").replace(da,function(b,d){c.push(d)}),c=(d.length===1?["require"]:["require","exports","module"]).concat(c));if(J&&(g=I||ba()))b||(b=g.getAttribute("data-requiremodule")),i=w[g.getAttribute("data-requirecontext")];(i?i.defQueue:P).push([b,c,d])};define.amd={jQuery:!0};g.exec=function(b){return eval(b)};g(n)}})(this); 35 | -------------------------------------------------------------------------------- /assets/js/lib/text.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license RequireJS text 2.0.5 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved. 3 | * Available via the MIT or new BSD license. 4 | * see: http://github.com/requirejs/text for details 5 | */ 6 | /*jslint regexp: true */ 7 | /*global require: false, XMLHttpRequest: false, ActiveXObject: false, 8 | define: false, window: false, process: false, Packages: false, 9 | java: false, location: false */ 10 | 11 | define(['module'], function (module) { 12 | 'use strict'; 13 | 14 | var text, fs, 15 | progIds = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'], 16 | xmlRegExp = /^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im, 17 | bodyRegExp = /]*>\s*([\s\S]+)\s*<\/body>/im, 18 | hasLocation = typeof location !== 'undefined' && location.href, 19 | defaultProtocol = hasLocation && location.protocol && location.protocol.replace(/\:/, ''), 20 | defaultHostName = hasLocation && location.hostname, 21 | defaultPort = hasLocation && (location.port || undefined), 22 | buildMap = [], 23 | masterConfig = (module.config && module.config()) || {}; 24 | 25 | text = { 26 | version: '2.0.5', 27 | 28 | strip: function (content) { 29 | //Strips declarations so that external SVG and XML 30 | //documents can be added to a document without worry. Also, if the string 31 | //is an HTML document, only the part inside the body tag is returned. 32 | if (content) { 33 | content = content.replace(xmlRegExp, ""); 34 | var matches = content.match(bodyRegExp); 35 | if (matches) { 36 | content = matches[1]; 37 | } 38 | } else { 39 | content = ""; 40 | } 41 | return content; 42 | }, 43 | 44 | jsEscape: function (content) { 45 | return content.replace(/(['\\])/g, '\\$1') 46 | .replace(/[\f]/g, "\\f") 47 | .replace(/[\b]/g, "\\b") 48 | .replace(/[\n]/g, "\\n") 49 | .replace(/[\t]/g, "\\t") 50 | .replace(/[\r]/g, "\\r") 51 | .replace(/[\u2028]/g, "\\u2028") 52 | .replace(/[\u2029]/g, "\\u2029"); 53 | }, 54 | 55 | createXhr: masterConfig.createXhr || function () { 56 | //Would love to dump the ActiveX crap in here. Need IE 6 to die first. 57 | var xhr, i, progId; 58 | if (typeof XMLHttpRequest !== "undefined") { 59 | return new XMLHttpRequest(); 60 | } else if (typeof ActiveXObject !== "undefined") { 61 | for (i = 0; i < 3; i += 1) { 62 | progId = progIds[i]; 63 | try { 64 | xhr = new ActiveXObject(progId); 65 | } catch (e) {} 66 | 67 | if (xhr) { 68 | progIds = [progId]; // so faster next time 69 | break; 70 | } 71 | } 72 | } 73 | 74 | return xhr; 75 | }, 76 | 77 | /** 78 | * Parses a resource name into its component parts. Resource names 79 | * look like: module/name.ext!strip, where the !strip part is 80 | * optional. 81 | * @param {String} name the resource name 82 | * @returns {Object} with properties "moduleName", "ext" and "strip" 83 | * where strip is a boolean. 84 | */ 85 | parseName: function (name) { 86 | var modName, ext, temp, 87 | strip = false, 88 | index = name.indexOf("."), 89 | isRelative = name.indexOf('./') === 0 || 90 | name.indexOf('../') === 0; 91 | 92 | if (index !== -1 && (!isRelative || index > 1)) { 93 | modName = name.substring(0, index); 94 | ext = name.substring(index + 1, name.length); 95 | } else { 96 | modName = name; 97 | } 98 | 99 | temp = ext || modName; 100 | index = temp.indexOf("!"); 101 | if (index !== -1) { 102 | //Pull off the strip arg. 103 | strip = temp.substring(index + 1) === "strip"; 104 | temp = temp.substring(0, index); 105 | if (ext) { 106 | ext = temp; 107 | } else { 108 | modName = temp; 109 | } 110 | } 111 | 112 | return { 113 | moduleName: modName, 114 | ext: ext, 115 | strip: strip 116 | }; 117 | }, 118 | 119 | xdRegExp: /^((\w+)\:)?\/\/([^\/\\]+)/, 120 | 121 | /** 122 | * Is an URL on another domain. Only works for browser use, returns 123 | * false in non-browser environments. Only used to know if an 124 | * optimized .js version of a text resource should be loaded 125 | * instead. 126 | * @param {String} url 127 | * @returns Boolean 128 | */ 129 | useXhr: function (url, protocol, hostname, port) { 130 | var uProtocol, uHostName, uPort, 131 | match = text.xdRegExp.exec(url); 132 | if (!match) { 133 | return true; 134 | } 135 | uProtocol = match[2]; 136 | uHostName = match[3]; 137 | 138 | uHostName = uHostName.split(':'); 139 | uPort = uHostName[1]; 140 | uHostName = uHostName[0]; 141 | 142 | return (!uProtocol || uProtocol === protocol) && 143 | (!uHostName || uHostName.toLowerCase() === hostname.toLowerCase()) && 144 | ((!uPort && !uHostName) || uPort === port); 145 | }, 146 | 147 | finishLoad: function (name, strip, content, onLoad) { 148 | content = strip ? text.strip(content) : content; 149 | if (masterConfig.isBuild) { 150 | buildMap[name] = content; 151 | } 152 | onLoad(content); 153 | }, 154 | 155 | load: function (name, req, onLoad, config) { 156 | //Name has format: some.module.filext!strip 157 | //The strip part is optional. 158 | //if strip is present, then that means only get the string contents 159 | //inside a body tag in an HTML string. For XML/SVG content it means 160 | //removing the declarations so the content can be inserted 161 | //into the current doc without problems. 162 | 163 | // Do not bother with the work if a build and text will 164 | // not be inlined. 165 | if (config.isBuild && !config.inlineText) { 166 | onLoad(); 167 | return; 168 | } 169 | 170 | masterConfig.isBuild = config.isBuild; 171 | 172 | var parsed = text.parseName(name), 173 | nonStripName = parsed.moduleName + 174 | (parsed.ext ? '.' + parsed.ext : ''), 175 | url = req.toUrl(nonStripName), 176 | useXhr = (masterConfig.useXhr) || 177 | text.useXhr; 178 | 179 | //Load the text. Use XHR if possible and in a browser. 180 | if (!hasLocation || useXhr(url, defaultProtocol, defaultHostName, defaultPort)) { 181 | text.get(url, function (content) { 182 | text.finishLoad(name, parsed.strip, content, onLoad); 183 | }, function (err) { 184 | if (onLoad.error) { 185 | onLoad.error(err); 186 | } 187 | }); 188 | } else { 189 | //Need to fetch the resource across domains. Assume 190 | //the resource has been optimized into a JS module. Fetch 191 | //by the module name + extension, but do not include the 192 | //!strip part to avoid file system issues. 193 | req([nonStripName], function (content) { 194 | text.finishLoad(parsed.moduleName + '.' + parsed.ext, 195 | parsed.strip, content, onLoad); 196 | }); 197 | } 198 | }, 199 | 200 | write: function (pluginName, moduleName, write, config) { 201 | if (buildMap.hasOwnProperty(moduleName)) { 202 | var content = text.jsEscape(buildMap[moduleName]); 203 | write.asModule(pluginName + "!" + moduleName, 204 | "define(function () { return '" + 205 | content + 206 | "';});\n"); 207 | } 208 | }, 209 | 210 | writeFile: function (pluginName, moduleName, req, write, config) { 211 | var parsed = text.parseName(moduleName), 212 | extPart = parsed.ext ? '.' + parsed.ext : '', 213 | nonStripName = parsed.moduleName + extPart, 214 | //Use a '.js' file name so that it indicates it is a 215 | //script that can be loaded across domains. 216 | fileName = req.toUrl(parsed.moduleName + extPart) + '.js'; 217 | 218 | //Leverage own load() method to load plugin value, but only 219 | //write out values that do not have the strip argument, 220 | //to avoid any potential issues with ! in file names. 221 | text.load(nonStripName, req, function (value) { 222 | //Use own write() method to construct full module value. 223 | //But need to create shell that translates writeFile's 224 | //write() to the right interface. 225 | var textWrite = function (contents) { 226 | return write(fileName, contents); 227 | }; 228 | textWrite.asModule = function (moduleName, contents) { 229 | return write.asModule(moduleName, fileName, contents); 230 | }; 231 | 232 | text.write(pluginName, nonStripName, textWrite, config); 233 | }, config); 234 | } 235 | }; 236 | 237 | if (masterConfig.env === 'node' || (!masterConfig.env && 238 | typeof process !== "undefined" && 239 | process.versions && 240 | !!process.versions.node)) { 241 | //Using special require.nodeRequire, something added by r.js. 242 | fs = require.nodeRequire('fs'); 243 | 244 | text.get = function (url, callback) { 245 | var file = fs.readFileSync(url, 'utf8'); 246 | //Remove BOM (Byte Mark Order) from utf8 files if it is there. 247 | if (file.indexOf('\uFEFF') === 0) { 248 | file = file.substring(1); 249 | } 250 | callback(file); 251 | }; 252 | } else if (masterConfig.env === 'xhr' || (!masterConfig.env && 253 | text.createXhr())) { 254 | text.get = function (url, callback, errback, headers) { 255 | var xhr = text.createXhr(), header; 256 | xhr.open('GET', url, true); 257 | 258 | //Allow plugins direct access to xhr headers 259 | if (headers) { 260 | for (header in headers) { 261 | if (headers.hasOwnProperty(header)) { 262 | xhr.setRequestHeader(header.toLowerCase(), headers[header]); 263 | } 264 | } 265 | } 266 | 267 | //Allow overrides specified in config 268 | if (masterConfig.onXhr) { 269 | masterConfig.onXhr(xhr, url); 270 | } 271 | 272 | xhr.onreadystatechange = function (evt) { 273 | var status, err; 274 | //Do not explicitly handle errors, those should be 275 | //visible via console output in the browser. 276 | if (xhr.readyState === 4) { 277 | status = xhr.status; 278 | if (status > 399 && status < 600) { 279 | //An http 4xx or 5xx error. Signal an error. 280 | err = new Error(url + ' HTTP status: ' + status); 281 | err.xhr = xhr; 282 | errback(err); 283 | } else { 284 | callback(xhr.responseText); 285 | } 286 | } 287 | }; 288 | xhr.send(null); 289 | }; 290 | } else if (masterConfig.env === 'rhino' || (!masterConfig.env && 291 | typeof Packages !== 'undefined' && typeof java !== 'undefined')) { 292 | //Why Java, why is this so awkward? 293 | text.get = function (url, callback) { 294 | var stringBuffer, line, 295 | encoding = "utf-8", 296 | file = new java.io.File(url), 297 | lineSeparator = java.lang.System.getProperty("line.separator"), 298 | input = new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream(file), encoding)), 299 | content = ''; 300 | try { 301 | stringBuffer = new java.lang.StringBuffer(); 302 | line = input.readLine(); 303 | 304 | // Byte Order Mark (BOM) - The Unicode Standard, version 3.0, page 324 305 | // http://www.unicode.org/faq/utf_bom.html 306 | 307 | // Note that when we use utf-8, the BOM should appear as "EF BB BF", but it doesn't due to this bug in the JDK: 308 | // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4508058 309 | if (line && line.length() && line.charAt(0) === 0xfeff) { 310 | // Eat the BOM, since we've already found the encoding on this file, 311 | // and we plan to concatenating this buffer with others; the BOM should 312 | // only appear at the top of a file. 313 | line = line.substring(1); 314 | } 315 | 316 | stringBuffer.append(line); 317 | 318 | while ((line = input.readLine()) !== null) { 319 | stringBuffer.append(lineSeparator); 320 | stringBuffer.append(line); 321 | } 322 | //Make sure we return a JavaScript string and not a Java string. 323 | content = String(stringBuffer.toString()); //String 324 | } finally { 325 | input.close(); 326 | } 327 | callback(content); 328 | }; 329 | } 330 | 331 | return text; 332 | }); 333 | -------------------------------------------------------------------------------- /assets/js/data/input.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "title": "Text Input", 4 | "fields": { 5 | "id": { 6 | "label": "ID / Name", 7 | "type": "input", 8 | "value": "textinput" 9 | }, 10 | "label": { 11 | "label": "Label Text", 12 | "type": "input", 13 | "value": "Text Input" 14 | }, 15 | "placeholder": { 16 | "label": "Placeholder", 17 | "type": "input", 18 | "value": "placeholder" 19 | }, 20 | "helptext": { 21 | "label": "Help Text", 22 | "type": "input", 23 | "value": "help" 24 | }, 25 | "required": { 26 | "label": "Required", 27 | "type": "checkbox", 28 | "value": false 29 | }, 30 | "inputsize": { 31 | "label": "Input Size", 32 | "type": "select", 33 | "value": [ 34 | { 35 | "value": "col-md-1", 36 | "label": "Mini", 37 | "selected": false 38 | }, 39 | { 40 | "value": "col-md-2", 41 | "label": "Small", 42 | "selected": false 43 | }, 44 | { 45 | "value": "col-md-4", 46 | "label": "Medium", 47 | "selected": true 48 | }, 49 | { 50 | "value": "col-md-5", 51 | "label": "Large", 52 | "selected": false 53 | }, 54 | { 55 | "value": "col-md-6", 56 | "label": "Xlarge", 57 | "selected": false 58 | }, 59 | { 60 | "value": "col-md-8", 61 | "label": "Xxlarge", 62 | "selected": false 63 | } 64 | ] 65 | } 66 | } 67 | }, 68 | { 69 | "title": "Date Input", 70 | "fields": { 71 | "id": { 72 | "label": "ID / Name", 73 | "type": "input", 74 | "value": "textinput" 75 | }, 76 | "label": { 77 | "label": "Label Text", 78 | "type": "input", 79 | "value": "Date Input" 80 | }, 81 | "placeholder": { 82 | "label": "Placeholder", 83 | "type": "input", 84 | "value": "placeholder" 85 | }, 86 | "helptext": { 87 | "label": "Help Text", 88 | "type": "input", 89 | "value": "help" 90 | }, 91 | "required": { 92 | "label": "Required", 93 | "type": "checkbox", 94 | "value": false 95 | }, 96 | "inputsize": { 97 | "label": "Input Size", 98 | "type": "select", 99 | "value": [ 100 | { 101 | "value": "col-md-1", 102 | "label": "Mini", 103 | "selected": false 104 | }, 105 | { 106 | "value": "col-md-2", 107 | "label": "Small", 108 | "selected": false 109 | }, 110 | { 111 | "value": "col-md-4", 112 | "label": "Medium", 113 | "selected": true 114 | }, 115 | { 116 | "value": "col-md-5", 117 | "label": "Large", 118 | "selected": false 119 | }, 120 | { 121 | "value": "col-md-6", 122 | "label": "Xlarge", 123 | "selected": false 124 | }, 125 | { 126 | "value": "col-md-8", 127 | "label": "Xxlarge", 128 | "selected": false 129 | } 130 | ] 131 | } 132 | } 133 | }, 134 | { 135 | "title": "Password Input", 136 | "fields": { 137 | "id": { 138 | "label": "ID / Name", 139 | "type": "input", 140 | "value": "passwordinput" 141 | }, 142 | "label": { 143 | "label": "Label Text", 144 | "type": "input", 145 | "value": "Password Input" 146 | }, 147 | "placeholder": { 148 | "label": "Placeholder", 149 | "type": "input", 150 | "value": "placeholder" 151 | }, 152 | "helptext": { 153 | "label": "Help Text", 154 | "type": "input", 155 | "value": "help" 156 | }, 157 | "required": { 158 | "label": "Required", 159 | "type": "checkbox", 160 | "value": false 161 | }, 162 | "inputsize": { 163 | "label": "Input Size", 164 | "type": "select", 165 | "value": [ 166 | { 167 | "value": "col-md-1", 168 | "label": "Mini", 169 | "selected": false 170 | }, 171 | { 172 | "value": "col-md-2", 173 | "label": "Small", 174 | "selected": false 175 | }, 176 | { 177 | "value": "col-md-4", 178 | "label": "Medium", 179 | "selected": true 180 | }, 181 | { 182 | "value": "col-md-5", 183 | "label": "Large", 184 | "selected": false 185 | }, 186 | { 187 | "value": "col-md-6", 188 | "label": "Xlarge", 189 | "selected": false 190 | }, 191 | { 192 | "value": "col-md-8", 193 | "label": "Xxlarge", 194 | "selected": false 195 | } 196 | ] 197 | } 198 | } 199 | }, 200 | { 201 | "title": "Search Input", 202 | "fields": { 203 | "id": { 204 | "label": "ID / Name", 205 | "type": "input", 206 | "value": "searchinput" 207 | }, 208 | "label": { 209 | "label": "Label Text", 210 | "type": "input", 211 | "value": "Search Input" 212 | }, 213 | "placeholder": { 214 | "label": "Placeholder", 215 | "type": "input", 216 | "value": "placeholder" 217 | }, 218 | "helptext": { 219 | "label": "Help Text", 220 | "type": "input", 221 | "value": "help" 222 | }, 223 | "required": { 224 | "label": "Required", 225 | "type": "checkbox", 226 | "value": false 227 | }, 228 | "inputsize": { 229 | "label": "Input Size", 230 | "type": "select", 231 | "value": [ 232 | { 233 | "value": "col-md-1", 234 | "label": "Mini", 235 | "selected": false 236 | }, 237 | { 238 | "value": "col-md-2", 239 | "label": "Small", 240 | "selected": false 241 | }, 242 | { 243 | "value": "col-md-4", 244 | "label": "Medium", 245 | "selected": true 246 | }, 247 | { 248 | "value": "col-md-5", 249 | "label": "Large", 250 | "selected": false 251 | }, 252 | { 253 | "value": "col-md-6", 254 | "label": "Xlarge", 255 | "selected": false 256 | }, 257 | { 258 | "value": "col-md-8", 259 | "label": "Xxlarge", 260 | "selected": false 261 | } 262 | ] 263 | } 264 | } 265 | }, 266 | { 267 | "title": "Prepended Text", 268 | "fields": { 269 | "id": { 270 | "label": "ID / Name", 271 | "type": "input", 272 | "value": "prependedtext" 273 | }, 274 | "label": { 275 | "label": "Label Text", 276 | "type": "input", 277 | "value": "Prepended Text" 278 | }, 279 | "prepend": { 280 | "label": "Prepend", 281 | "type": "input", 282 | "value": "prepend" 283 | }, 284 | "placeholder": { 285 | "label": "Placeholder", 286 | "type": "input", 287 | "value": "placeholder" 288 | }, 289 | "helptext": { 290 | "label": "Help Text", 291 | "type": "input", 292 | "value": "help" 293 | }, 294 | "required": { 295 | "label": "Required", 296 | "type": "checkbox", 297 | "value": false 298 | }, 299 | "inputsize": { 300 | "label": "Input Size", 301 | "type": "select", 302 | "value": [ 303 | { 304 | "value": "col-md-1", 305 | "label": "Mini", 306 | "selected": false 307 | }, 308 | { 309 | "value": "col-md-2", 310 | "label": "Small", 311 | "selected": false 312 | }, 313 | { 314 | "value": "col-md-4", 315 | "label": "Medium", 316 | "selected": true 317 | }, 318 | { 319 | "value": "col-md-5", 320 | "label": "Large", 321 | "selected": false 322 | }, 323 | { 324 | "value": "col-md-6", 325 | "label": "Xlarge", 326 | "selected": false 327 | }, 328 | { 329 | "value": "col-md-8", 330 | "label": "Xxlarge", 331 | "selected": false 332 | } 333 | ] 334 | } 335 | } 336 | }, 337 | { 338 | "title": "Appended Text", 339 | "fields": { 340 | "id": { 341 | "label": "ID / Name", 342 | "type": "input", 343 | "value": "appendedtext" 344 | }, 345 | "label": { 346 | "label": "Label Text", 347 | "type": "input", 348 | "value": "Appended Text" 349 | }, 350 | "append": { 351 | "label": "Append", 352 | "type": "input", 353 | "value": "append" 354 | }, 355 | "placeholder": { 356 | "label": "Placeholder", 357 | "type": "input", 358 | "value": "placeholder" 359 | }, 360 | "helptext": { 361 | "label": "Help Text", 362 | "type": "input", 363 | "value": "help" 364 | }, 365 | "required": { 366 | "label": "Required", 367 | "type": "checkbox", 368 | "value": false 369 | }, 370 | "inputsize": { 371 | "label": "Input Size", 372 | "type": "select", 373 | "value": [ 374 | { 375 | "value": "col-md-1", 376 | "label": "Mini", 377 | "selected": false 378 | }, 379 | { 380 | "value": "col-md-2", 381 | "label": "Small", 382 | "selected": false 383 | }, 384 | { 385 | "value": "col-md-4", 386 | "label": "Medium", 387 | "selected": true 388 | }, 389 | { 390 | "value": "col-md-5", 391 | "label": "Large", 392 | "selected": false 393 | }, 394 | { 395 | "value": "col-md-6", 396 | "label": "Xlarge", 397 | "selected": false 398 | }, 399 | { 400 | "value": "col-md-8", 401 | "label": "Xxlarge", 402 | "selected": false 403 | } 404 | ] 405 | } 406 | } 407 | }, 408 | { 409 | "title": "Prepended Checkbox", 410 | "fields": { 411 | "id": { 412 | "label": "ID / Name", 413 | "type": "input", 414 | "value": "prependedcheckbox" 415 | }, 416 | "label": { 417 | "label": "Label Text", 418 | "type": "input", 419 | "value": "Prepended Checkbox" 420 | }, 421 | "placeholder": { 422 | "label": "Placeholder", 423 | "type": "input", 424 | "value": "placeholder" 425 | }, 426 | "helptext": { 427 | "label": "Help Text", 428 | "type": "input", 429 | "value": "help" 430 | }, 431 | "checked": { 432 | "label": "Checked", 433 | "type": "checkbox", 434 | "value": false 435 | }, 436 | "required": { 437 | "label": "Required", 438 | "type": "checkbox", 439 | "value": false 440 | }, 441 | "inputsize": { 442 | "label": "Input Size", 443 | "type": "select", 444 | "value": [ 445 | { 446 | "value": "col-md-1", 447 | "label": "Mini", 448 | "selected": false 449 | }, 450 | { 451 | "value": "col-md-2", 452 | "label": "Small", 453 | "selected": false 454 | }, 455 | { 456 | "value": "col-md-4", 457 | "label": "Medium", 458 | "selected": true 459 | }, 460 | { 461 | "value": "col-md-5", 462 | "label": "Large", 463 | "selected": false 464 | }, 465 | { 466 | "value": "col-md-6", 467 | "label": "Xlarge", 468 | "selected": false 469 | }, 470 | { 471 | "value": "col-md-8", 472 | "label": "Xxlarge", 473 | "selected": false 474 | } 475 | ] 476 | } 477 | } 478 | }, 479 | { 480 | "title": "Appended Checkbox", 481 | "fields": { 482 | "id": { 483 | "label": "ID / Name", 484 | "type": "input", 485 | "value": "appendedcheckbox" 486 | }, 487 | "label": { 488 | "label": "Label Text", 489 | "type": "input", 490 | "value": "Appended Checkbox" 491 | }, 492 | "placeholder": { 493 | "label": "Placeholder", 494 | "type": "input", 495 | "value": "placeholder" 496 | }, 497 | "helptext": { 498 | "label": "Help Text", 499 | "type": "input", 500 | "value": "help" 501 | }, 502 | "checked": { 503 | "label": "Checked", 504 | "type": "checkbox", 505 | "value": false 506 | }, 507 | "required": { 508 | "label": "Required", 509 | "type": "checkbox", 510 | "value": false 511 | }, 512 | "inputsize": { 513 | "label": "Input Size", 514 | "type": "select", 515 | "value": [ 516 | { 517 | "value": "col-md-1", 518 | "label": "Mini", 519 | "selected": false 520 | }, 521 | { 522 | "value": "col-md-2", 523 | "label": "Small", 524 | "selected": false 525 | }, 526 | { 527 | "value": "col-md-4", 528 | "label": "Medium", 529 | "selected": true 530 | }, 531 | { 532 | "value": "col-md-5", 533 | "label": "Large", 534 | "selected": false 535 | }, 536 | { 537 | "value": "col-md-6", 538 | "label": "Xlarge", 539 | "selected": false 540 | }, 541 | { 542 | "value": "col-md-8", 543 | "label": "Xxlarge", 544 | "selected": false 545 | } 546 | ] 547 | } 548 | } 549 | }, 550 | { 551 | "title": "Button Drop Down", 552 | "fields": { 553 | "id": { 554 | "label": "ID / Name", 555 | "type": "input", 556 | "value": "buttondropdown" 557 | }, 558 | "label": { 559 | "label": "Label Text", 560 | "type": "input", 561 | "value": "Button Drop Down" 562 | }, 563 | "placeholder": { 564 | "label": "Placeholder", 565 | "type": "input", 566 | "value": "placeholder" 567 | }, 568 | "buttontext": { 569 | "label": "Button Text", 570 | "type": "input", 571 | "value": "Action" 572 | }, 573 | "buttonoptions": { 574 | "label": "Options", 575 | "type": "textarea-split", 576 | "value": [ 577 | "Option one", 578 | "Option two", 579 | "Option three" 580 | ] 581 | }, 582 | "required": { 583 | "label": "Required", 584 | "type": "checkbox", 585 | "value": false 586 | }, 587 | "inputsize": { 588 | "label": "Input Size", 589 | "type": "select", 590 | "value": [ 591 | { 592 | "value": "col-md-1", 593 | "label": "Mini", 594 | "selected": false 595 | }, 596 | { 597 | "value": "col-md-2", 598 | "label": "Small", 599 | "selected": false 600 | }, 601 | { 602 | "value": "col-md-4", 603 | "label": "Medium", 604 | "selected": true 605 | }, 606 | { 607 | "value": "col-md-5", 608 | "label": "Large", 609 | "selected": false 610 | }, 611 | { 612 | "value": "col-md-6", 613 | "label": "Xlarge", 614 | "selected": false 615 | }, 616 | { 617 | "value": "col-md-8", 618 | "label": "Xxlarge", 619 | "selected": false 620 | } 621 | ] 622 | } 623 | } 624 | }, 625 | { 626 | "title": "Text Area", 627 | "fields": { 628 | "id": { 629 | "label": "ID / Name", 630 | "type": "input", 631 | "value": "textarea" 632 | }, 633 | "label": { 634 | "label": "Label Text", 635 | "type": "input", 636 | "value": "Text Area" 637 | }, 638 | "textarea": { 639 | "label": "Starting Text", 640 | "type": "textarea", 641 | "value": "default text" 642 | } 643 | } 644 | } 645 | ] 646 | -------------------------------------------------------------------------------- /assets/js/lib/underscore.js: -------------------------------------------------------------------------------- 1 | // Underscore.js 1.4.4 2 | // http://underscorejs.org 3 | // (c) 2009-2013 Jeremy Ashkenas, DocumentCloud Inc. 4 | // Underscore may be freely distributed under the MIT license. 5 | 6 | (function() { 7 | 8 | // Baseline setup 9 | // -------------- 10 | 11 | // Establish the root object, `window` in the browser, or `global` on the server. 12 | var root = this; 13 | 14 | // Save the previous value of the `_` variable. 15 | var previousUnderscore = root._; 16 | 17 | // Establish the object that gets returned to break out of a loop iteration. 18 | var breaker = {}; 19 | 20 | // Save bytes in the minified (but not gzipped) version: 21 | var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; 22 | 23 | // Create quick reference variables for speed access to core prototypes. 24 | var push = ArrayProto.push, 25 | slice = ArrayProto.slice, 26 | concat = ArrayProto.concat, 27 | toString = ObjProto.toString, 28 | hasOwnProperty = ObjProto.hasOwnProperty; 29 | 30 | // All **ECMAScript 5** native function implementations that we hope to use 31 | // are declared here. 32 | var 33 | nativeForEach = ArrayProto.forEach, 34 | nativeMap = ArrayProto.map, 35 | nativeReduce = ArrayProto.reduce, 36 | nativeReduceRight = ArrayProto.reduceRight, 37 | nativeFilter = ArrayProto.filter, 38 | nativeEvery = ArrayProto.every, 39 | nativeSome = ArrayProto.some, 40 | nativeIndexOf = ArrayProto.indexOf, 41 | nativeLastIndexOf = ArrayProto.lastIndexOf, 42 | nativeIsArray = Array.isArray, 43 | nativeKeys = Object.keys, 44 | nativeBind = FuncProto.bind; 45 | 46 | // Create a safe reference to the Underscore object for use below. 47 | var _ = function(obj) { 48 | if (obj instanceof _) return obj; 49 | if (!(this instanceof _)) return new _(obj); 50 | this._wrapped = obj; 51 | }; 52 | 53 | // Export the Underscore object for **Node.js**, with 54 | // backwards-compatibility for the old `require()` API. If we're in 55 | // the browser, add `_` as a global object via a string identifier, 56 | // for Closure Compiler "advanced" mode. 57 | if (typeof exports !== 'undefined') { 58 | if (typeof module !== 'undefined' && module.exports) { 59 | exports = module.exports = _; 60 | } 61 | exports._ = _; 62 | } else { 63 | root._ = _; 64 | } 65 | 66 | // Current version. 67 | _.VERSION = '1.4.4'; 68 | 69 | // Collection Functions 70 | // -------------------- 71 | 72 | // The cornerstone, an `each` implementation, aka `forEach`. 73 | // Handles objects with the built-in `forEach`, arrays, and raw objects. 74 | // Delegates to **ECMAScript 5**'s native `forEach` if available. 75 | var each = _.each = _.forEach = function(obj, iterator, context) { 76 | if (obj == null) return; 77 | if (nativeForEach && obj.forEach === nativeForEach) { 78 | obj.forEach(iterator, context); 79 | } else if (obj.length === +obj.length) { 80 | for (var i = 0, l = obj.length; i < l; i++) { 81 | if (iterator.call(context, obj[i], i, obj) === breaker) return; 82 | } 83 | } else { 84 | for (var key in obj) { 85 | if (_.has(obj, key)) { 86 | if (iterator.call(context, obj[key], key, obj) === breaker) return; 87 | } 88 | } 89 | } 90 | }; 91 | 92 | // Return the results of applying the iterator to each element. 93 | // Delegates to **ECMAScript 5**'s native `map` if available. 94 | _.map = _.collect = function(obj, iterator, context) { 95 | var results = []; 96 | if (obj == null) return results; 97 | if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context); 98 | each(obj, function(value, index, list) { 99 | results[results.length] = iterator.call(context, value, index, list); 100 | }); 101 | return results; 102 | }; 103 | 104 | var reduceError = 'Reduce of empty array with no initial value'; 105 | 106 | // **Reduce** builds up a single result from a list of values, aka `inject`, 107 | // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available. 108 | _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) { 109 | var initial = arguments.length > 2; 110 | if (obj == null) obj = []; 111 | if (nativeReduce && obj.reduce === nativeReduce) { 112 | if (context) iterator = _.bind(iterator, context); 113 | return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator); 114 | } 115 | each(obj, function(value, index, list) { 116 | if (!initial) { 117 | memo = value; 118 | initial = true; 119 | } else { 120 | memo = iterator.call(context, memo, value, index, list); 121 | } 122 | }); 123 | if (!initial) throw new TypeError(reduceError); 124 | return memo; 125 | }; 126 | 127 | // The right-associative version of reduce, also known as `foldr`. 128 | // Delegates to **ECMAScript 5**'s native `reduceRight` if available. 129 | _.reduceRight = _.foldr = function(obj, iterator, memo, context) { 130 | var initial = arguments.length > 2; 131 | if (obj == null) obj = []; 132 | if (nativeReduceRight && obj.reduceRight === nativeReduceRight) { 133 | if (context) iterator = _.bind(iterator, context); 134 | return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator); 135 | } 136 | var length = obj.length; 137 | if (length !== +length) { 138 | var keys = _.keys(obj); 139 | length = keys.length; 140 | } 141 | each(obj, function(value, index, list) { 142 | index = keys ? keys[--length] : --length; 143 | if (!initial) { 144 | memo = obj[index]; 145 | initial = true; 146 | } else { 147 | memo = iterator.call(context, memo, obj[index], index, list); 148 | } 149 | }); 150 | if (!initial) throw new TypeError(reduceError); 151 | return memo; 152 | }; 153 | 154 | // Return the first value which passes a truth test. Aliased as `detect`. 155 | _.find = _.detect = function(obj, iterator, context) { 156 | var result; 157 | any(obj, function(value, index, list) { 158 | if (iterator.call(context, value, index, list)) { 159 | result = value; 160 | return true; 161 | } 162 | }); 163 | return result; 164 | }; 165 | 166 | // Return all the elements that pass a truth test. 167 | // Delegates to **ECMAScript 5**'s native `filter` if available. 168 | // Aliased as `select`. 169 | _.filter = _.select = function(obj, iterator, context) { 170 | var results = []; 171 | if (obj == null) return results; 172 | if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context); 173 | each(obj, function(value, index, list) { 174 | if (iterator.call(context, value, index, list)) results[results.length] = value; 175 | }); 176 | return results; 177 | }; 178 | 179 | // Return all the elements for which a truth test fails. 180 | _.reject = function(obj, iterator, context) { 181 | return _.filter(obj, function(value, index, list) { 182 | return !iterator.call(context, value, index, list); 183 | }, context); 184 | }; 185 | 186 | // Determine whether all of the elements match a truth test. 187 | // Delegates to **ECMAScript 5**'s native `every` if available. 188 | // Aliased as `all`. 189 | _.every = _.all = function(obj, iterator, context) { 190 | iterator || (iterator = _.identity); 191 | var result = true; 192 | if (obj == null) return result; 193 | if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context); 194 | each(obj, function(value, index, list) { 195 | if (!(result = result && iterator.call(context, value, index, list))) return breaker; 196 | }); 197 | return !!result; 198 | }; 199 | 200 | // Determine if at least one element in the object matches a truth test. 201 | // Delegates to **ECMAScript 5**'s native `some` if available. 202 | // Aliased as `any`. 203 | var any = _.some = _.any = function(obj, iterator, context) { 204 | iterator || (iterator = _.identity); 205 | var result = false; 206 | if (obj == null) return result; 207 | if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context); 208 | each(obj, function(value, index, list) { 209 | if (result || (result = iterator.call(context, value, index, list))) return breaker; 210 | }); 211 | return !!result; 212 | }; 213 | 214 | // Determine if the array or object contains a given value (using `===`). 215 | // Aliased as `include`. 216 | _.contains = _.include = function(obj, target) { 217 | if (obj == null) return false; 218 | if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1; 219 | return any(obj, function(value) { 220 | return value === target; 221 | }); 222 | }; 223 | 224 | // Invoke a method (with arguments) on every item in a collection. 225 | _.invoke = function(obj, method) { 226 | var args = slice.call(arguments, 2); 227 | var isFunc = _.isFunction(method); 228 | return _.map(obj, function(value) { 229 | return (isFunc ? method : value[method]).apply(value, args); 230 | }); 231 | }; 232 | 233 | // Convenience version of a common use case of `map`: fetching a property. 234 | _.pluck = function(obj, key) { 235 | return _.map(obj, function(value){ return value[key]; }); 236 | }; 237 | 238 | // Convenience version of a common use case of `filter`: selecting only objects 239 | // containing specific `key:value` pairs. 240 | _.where = function(obj, attrs, first) { 241 | if (_.isEmpty(attrs)) return first ? null : []; 242 | return _[first ? 'find' : 'filter'](obj, function(value) { 243 | for (var key in attrs) { 244 | if (attrs[key] !== value[key]) return false; 245 | } 246 | return true; 247 | }); 248 | }; 249 | 250 | // Convenience version of a common use case of `find`: getting the first object 251 | // containing specific `key:value` pairs. 252 | _.findWhere = function(obj, attrs) { 253 | return _.where(obj, attrs, true); 254 | }; 255 | 256 | // Return the maximum element or (element-based computation). 257 | // Can't optimize arrays of integers longer than 65,535 elements. 258 | // See: https://bugs.webkit.org/show_bug.cgi?id=80797 259 | _.max = function(obj, iterator, context) { 260 | if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { 261 | return Math.max.apply(Math, obj); 262 | } 263 | if (!iterator && _.isEmpty(obj)) return -Infinity; 264 | var result = {computed : -Infinity, value: -Infinity}; 265 | each(obj, function(value, index, list) { 266 | var computed = iterator ? iterator.call(context, value, index, list) : value; 267 | computed >= result.computed && (result = {value : value, computed : computed}); 268 | }); 269 | return result.value; 270 | }; 271 | 272 | // Return the minimum element (or element-based computation). 273 | _.min = function(obj, iterator, context) { 274 | if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { 275 | return Math.min.apply(Math, obj); 276 | } 277 | if (!iterator && _.isEmpty(obj)) return Infinity; 278 | var result = {computed : Infinity, value: Infinity}; 279 | each(obj, function(value, index, list) { 280 | var computed = iterator ? iterator.call(context, value, index, list) : value; 281 | computed < result.computed && (result = {value : value, computed : computed}); 282 | }); 283 | return result.value; 284 | }; 285 | 286 | // Shuffle an array. 287 | _.shuffle = function(obj) { 288 | var rand; 289 | var index = 0; 290 | var shuffled = []; 291 | each(obj, function(value) { 292 | rand = _.random(index++); 293 | shuffled[index - 1] = shuffled[rand]; 294 | shuffled[rand] = value; 295 | }); 296 | return shuffled; 297 | }; 298 | 299 | // An internal function to generate lookup iterators. 300 | var lookupIterator = function(value) { 301 | return _.isFunction(value) ? value : function(obj){ return obj[value]; }; 302 | }; 303 | 304 | // Sort the object's values by a criterion produced by an iterator. 305 | _.sortBy = function(obj, value, context) { 306 | var iterator = lookupIterator(value); 307 | return _.pluck(_.map(obj, function(value, index, list) { 308 | return { 309 | value : value, 310 | index : index, 311 | criteria : iterator.call(context, value, index, list) 312 | }; 313 | }).sort(function(left, right) { 314 | var a = left.criteria; 315 | var b = right.criteria; 316 | if (a !== b) { 317 | if (a > b || a === void 0) return 1; 318 | if (a < b || b === void 0) return -1; 319 | } 320 | return left.index < right.index ? -1 : 1; 321 | }), 'value'); 322 | }; 323 | 324 | // An internal function used for aggregate "group by" operations. 325 | var group = function(obj, value, context, behavior) { 326 | var result = {}; 327 | var iterator = lookupIterator(value || _.identity); 328 | each(obj, function(value, index) { 329 | var key = iterator.call(context, value, index, obj); 330 | behavior(result, key, value); 331 | }); 332 | return result; 333 | }; 334 | 335 | // Groups the object's values by a criterion. Pass either a string attribute 336 | // to group by, or a function that returns the criterion. 337 | _.groupBy = function(obj, value, context) { 338 | return group(obj, value, context, function(result, key, value) { 339 | (_.has(result, key) ? result[key] : (result[key] = [])).push(value); 340 | }); 341 | }; 342 | 343 | // Counts instances of an object that group by a certain criterion. Pass 344 | // either a string attribute to count by, or a function that returns the 345 | // criterion. 346 | _.countBy = function(obj, value, context) { 347 | return group(obj, value, context, function(result, key) { 348 | if (!_.has(result, key)) result[key] = 0; 349 | result[key]++; 350 | }); 351 | }; 352 | 353 | // Use a comparator function to figure out the smallest index at which 354 | // an object should be inserted so as to maintain order. Uses binary search. 355 | _.sortedIndex = function(array, obj, iterator, context) { 356 | iterator = iterator == null ? _.identity : lookupIterator(iterator); 357 | var value = iterator.call(context, obj); 358 | var low = 0, high = array.length; 359 | while (low < high) { 360 | var mid = (low + high) >>> 1; 361 | iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid; 362 | } 363 | return low; 364 | }; 365 | 366 | // Safely convert anything iterable into a real, live array. 367 | _.toArray = function(obj) { 368 | if (!obj) return []; 369 | if (_.isArray(obj)) return slice.call(obj); 370 | if (obj.length === +obj.length) return _.map(obj, _.identity); 371 | return _.values(obj); 372 | }; 373 | 374 | // Return the number of elements in an object. 375 | _.size = function(obj) { 376 | if (obj == null) return 0; 377 | return (obj.length === +obj.length) ? obj.length : _.keys(obj).length; 378 | }; 379 | 380 | // Array Functions 381 | // --------------- 382 | 383 | // Get the first element of an array. Passing **n** will return the first N 384 | // values in the array. Aliased as `head` and `take`. The **guard** check 385 | // allows it to work with `_.map`. 386 | _.first = _.head = _.take = function(array, n, guard) { 387 | if (array == null) return void 0; 388 | return (n != null) && !guard ? slice.call(array, 0, n) : array[0]; 389 | }; 390 | 391 | // Returns everything but the last entry of the array. Especially useful on 392 | // the arguments object. Passing **n** will return all the values in 393 | // the array, excluding the last N. The **guard** check allows it to work with 394 | // `_.map`. 395 | _.initial = function(array, n, guard) { 396 | return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n)); 397 | }; 398 | 399 | // Get the last element of an array. Passing **n** will return the last N 400 | // values in the array. The **guard** check allows it to work with `_.map`. 401 | _.last = function(array, n, guard) { 402 | if (array == null) return void 0; 403 | if ((n != null) && !guard) { 404 | return slice.call(array, Math.max(array.length - n, 0)); 405 | } else { 406 | return array[array.length - 1]; 407 | } 408 | }; 409 | 410 | // Returns everything but the first entry of the array. Aliased as `tail` and `drop`. 411 | // Especially useful on the arguments object. Passing an **n** will return 412 | // the rest N values in the array. The **guard** 413 | // check allows it to work with `_.map`. 414 | _.rest = _.tail = _.drop = function(array, n, guard) { 415 | return slice.call(array, (n == null) || guard ? 1 : n); 416 | }; 417 | 418 | // Trim out all falsy values from an array. 419 | _.compact = function(array) { 420 | return _.filter(array, _.identity); 421 | }; 422 | 423 | // Internal implementation of a recursive `flatten` function. 424 | var flatten = function(input, shallow, output) { 425 | each(input, function(value) { 426 | if (_.isArray(value)) { 427 | shallow ? push.apply(output, value) : flatten(value, shallow, output); 428 | } else { 429 | output.push(value); 430 | } 431 | }); 432 | return output; 433 | }; 434 | 435 | // Return a completely flattened version of an array. 436 | _.flatten = function(array, shallow) { 437 | return flatten(array, shallow, []); 438 | }; 439 | 440 | // Return a version of the array that does not contain the specified value(s). 441 | _.without = function(array) { 442 | return _.difference(array, slice.call(arguments, 1)); 443 | }; 444 | 445 | // Produce a duplicate-free version of the array. If the array has already 446 | // been sorted, you have the option of using a faster algorithm. 447 | // Aliased as `unique`. 448 | _.uniq = _.unique = function(array, isSorted, iterator, context) { 449 | if (_.isFunction(isSorted)) { 450 | context = iterator; 451 | iterator = isSorted; 452 | isSorted = false; 453 | } 454 | var initial = iterator ? _.map(array, iterator, context) : array; 455 | var results = []; 456 | var seen = []; 457 | each(initial, function(value, index) { 458 | if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) { 459 | seen.push(value); 460 | results.push(array[index]); 461 | } 462 | }); 463 | return results; 464 | }; 465 | 466 | // Produce an array that contains the union: each distinct element from all of 467 | // the passed-in arrays. 468 | _.union = function() { 469 | return _.uniq(concat.apply(ArrayProto, arguments)); 470 | }; 471 | 472 | // Produce an array that contains every item shared between all the 473 | // passed-in arrays. 474 | _.intersection = function(array) { 475 | var rest = slice.call(arguments, 1); 476 | return _.filter(_.uniq(array), function(item) { 477 | return _.every(rest, function(other) { 478 | return _.indexOf(other, item) >= 0; 479 | }); 480 | }); 481 | }; 482 | 483 | // Take the difference between one array and a number of other arrays. 484 | // Only the elements present in just the first array will remain. 485 | _.difference = function(array) { 486 | var rest = concat.apply(ArrayProto, slice.call(arguments, 1)); 487 | return _.filter(array, function(value){ return !_.contains(rest, value); }); 488 | }; 489 | 490 | // Zip together multiple lists into a single array -- elements that share 491 | // an index go together. 492 | _.zip = function() { 493 | var args = slice.call(arguments); 494 | var length = _.max(_.pluck(args, 'length')); 495 | var results = new Array(length); 496 | for (var i = 0; i < length; i++) { 497 | results[i] = _.pluck(args, "" + i); 498 | } 499 | return results; 500 | }; 501 | 502 | // Converts lists into objects. Pass either a single array of `[key, value]` 503 | // pairs, or two parallel arrays of the same length -- one of keys, and one of 504 | // the corresponding values. 505 | _.object = function(list, values) { 506 | if (list == null) return {}; 507 | var result = {}; 508 | for (var i = 0, l = list.length; i < l; i++) { 509 | if (values) { 510 | result[list[i]] = values[i]; 511 | } else { 512 | result[list[i][0]] = list[i][1]; 513 | } 514 | } 515 | return result; 516 | }; 517 | 518 | // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**), 519 | // we need this function. Return the position of the first occurrence of an 520 | // item in an array, or -1 if the item is not included in the array. 521 | // Delegates to **ECMAScript 5**'s native `indexOf` if available. 522 | // If the array is large and already in sort order, pass `true` 523 | // for **isSorted** to use binary search. 524 | _.indexOf = function(array, item, isSorted) { 525 | if (array == null) return -1; 526 | var i = 0, l = array.length; 527 | if (isSorted) { 528 | if (typeof isSorted == 'number') { 529 | i = (isSorted < 0 ? Math.max(0, l + isSorted) : isSorted); 530 | } else { 531 | i = _.sortedIndex(array, item); 532 | return array[i] === item ? i : -1; 533 | } 534 | } 535 | if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted); 536 | for (; i < l; i++) if (array[i] === item) return i; 537 | return -1; 538 | }; 539 | 540 | // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available. 541 | _.lastIndexOf = function(array, item, from) { 542 | if (array == null) return -1; 543 | var hasIndex = from != null; 544 | if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) { 545 | return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item); 546 | } 547 | var i = (hasIndex ? from : array.length); 548 | while (i--) if (array[i] === item) return i; 549 | return -1; 550 | }; 551 | 552 | // Generate an integer Array containing an arithmetic progression. A port of 553 | // the native Python `range()` function. See 554 | // [the Python documentation](http://docs.python.org/library/functions.html#range). 555 | _.range = function(start, stop, step) { 556 | if (arguments.length <= 1) { 557 | stop = start || 0; 558 | start = 0; 559 | } 560 | step = arguments[2] || 1; 561 | 562 | var len = Math.max(Math.ceil((stop - start) / step), 0); 563 | var idx = 0; 564 | var range = new Array(len); 565 | 566 | while(idx < len) { 567 | range[idx++] = start; 568 | start += step; 569 | } 570 | 571 | return range; 572 | }; 573 | 574 | // Function (ahem) Functions 575 | // ------------------ 576 | 577 | // Create a function bound to a given object (assigning `this`, and arguments, 578 | // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if 579 | // available. 580 | _.bind = function(func, context) { 581 | if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); 582 | var args = slice.call(arguments, 2); 583 | return function() { 584 | return func.apply(context, args.concat(slice.call(arguments))); 585 | }; 586 | }; 587 | 588 | // Partially apply a function by creating a version that has had some of its 589 | // arguments pre-filled, without changing its dynamic `this` context. 590 | _.partial = function(func) { 591 | var args = slice.call(arguments, 1); 592 | return function() { 593 | return func.apply(this, args.concat(slice.call(arguments))); 594 | }; 595 | }; 596 | 597 | // Bind all of an object's methods to that object. Useful for ensuring that 598 | // all callbacks defined on an object belong to it. 599 | _.bindAll = function(obj) { 600 | var funcs = slice.call(arguments, 1); 601 | if (funcs.length === 0) funcs = _.functions(obj); 602 | each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); }); 603 | return obj; 604 | }; 605 | 606 | // Memoize an expensive function by storing its results. 607 | _.memoize = function(func, hasher) { 608 | var memo = {}; 609 | hasher || (hasher = _.identity); 610 | return function() { 611 | var key = hasher.apply(this, arguments); 612 | return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments)); 613 | }; 614 | }; 615 | 616 | // Delays a function for the given number of milliseconds, and then calls 617 | // it with the arguments supplied. 618 | _.delay = function(func, wait) { 619 | var args = slice.call(arguments, 2); 620 | return setTimeout(function(){ return func.apply(null, args); }, wait); 621 | }; 622 | 623 | // Defers a function, scheduling it to run after the current call stack has 624 | // cleared. 625 | _.defer = function(func) { 626 | return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1))); 627 | }; 628 | 629 | // Returns a function, that, when invoked, will only be triggered at most once 630 | // during a given window of time. 631 | _.throttle = function(func, wait) { 632 | var context, args, timeout, result; 633 | var previous = 0; 634 | var later = function() { 635 | previous = new Date; 636 | timeout = null; 637 | result = func.apply(context, args); 638 | }; 639 | return function() { 640 | var now = new Date; 641 | var remaining = wait - (now - previous); 642 | context = this; 643 | args = arguments; 644 | if (remaining <= 0) { 645 | clearTimeout(timeout); 646 | timeout = null; 647 | previous = now; 648 | result = func.apply(context, args); 649 | } else if (!timeout) { 650 | timeout = setTimeout(later, remaining); 651 | } 652 | return result; 653 | }; 654 | }; 655 | 656 | // Returns a function, that, as long as it continues to be invoked, will not 657 | // be triggered. The function will be called after it stops being called for 658 | // N milliseconds. If `immediate` is passed, trigger the function on the 659 | // leading edge, instead of the trailing. 660 | _.debounce = function(func, wait, immediate) { 661 | var timeout, result; 662 | return function() { 663 | var context = this, args = arguments; 664 | var later = function() { 665 | timeout = null; 666 | if (!immediate) result = func.apply(context, args); 667 | }; 668 | var callNow = immediate && !timeout; 669 | clearTimeout(timeout); 670 | timeout = setTimeout(later, wait); 671 | if (callNow) result = func.apply(context, args); 672 | return result; 673 | }; 674 | }; 675 | 676 | // Returns a function that will be executed at most one time, no matter how 677 | // often you call it. Useful for lazy initialization. 678 | _.once = function(func) { 679 | var ran = false, memo; 680 | return function() { 681 | if (ran) return memo; 682 | ran = true; 683 | memo = func.apply(this, arguments); 684 | func = null; 685 | return memo; 686 | }; 687 | }; 688 | 689 | // Returns the first function passed as an argument to the second, 690 | // allowing you to adjust arguments, run code before and after, and 691 | // conditionally execute the original function. 692 | _.wrap = function(func, wrapper) { 693 | return function() { 694 | var args = [func]; 695 | push.apply(args, arguments); 696 | return wrapper.apply(this, args); 697 | }; 698 | }; 699 | 700 | // Returns a function that is the composition of a list of functions, each 701 | // consuming the return value of the function that follows. 702 | _.compose = function() { 703 | var funcs = arguments; 704 | return function() { 705 | var args = arguments; 706 | for (var i = funcs.length - 1; i >= 0; i--) { 707 | args = [funcs[i].apply(this, args)]; 708 | } 709 | return args[0]; 710 | }; 711 | }; 712 | 713 | // Returns a function that will only be executed after being called N times. 714 | _.after = function(times, func) { 715 | if (times <= 0) return func(); 716 | return function() { 717 | if (--times < 1) { 718 | return func.apply(this, arguments); 719 | } 720 | }; 721 | }; 722 | 723 | // Object Functions 724 | // ---------------- 725 | 726 | // Retrieve the names of an object's properties. 727 | // Delegates to **ECMAScript 5**'s native `Object.keys` 728 | _.keys = nativeKeys || function(obj) { 729 | if (obj !== Object(obj)) throw new TypeError('Invalid object'); 730 | var keys = []; 731 | for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key; 732 | return keys; 733 | }; 734 | 735 | // Retrieve the values of an object's properties. 736 | _.values = function(obj) { 737 | var values = []; 738 | for (var key in obj) if (_.has(obj, key)) values.push(obj[key]); 739 | return values; 740 | }; 741 | 742 | // Convert an object into a list of `[key, value]` pairs. 743 | _.pairs = function(obj) { 744 | var pairs = []; 745 | for (var key in obj) if (_.has(obj, key)) pairs.push([key, obj[key]]); 746 | return pairs; 747 | }; 748 | 749 | // Invert the keys and values of an object. The values must be serializable. 750 | _.invert = function(obj) { 751 | var result = {}; 752 | for (var key in obj) if (_.has(obj, key)) result[obj[key]] = key; 753 | return result; 754 | }; 755 | 756 | // Return a sorted list of the function names available on the object. 757 | // Aliased as `methods` 758 | _.functions = _.methods = function(obj) { 759 | var names = []; 760 | for (var key in obj) { 761 | if (_.isFunction(obj[key])) names.push(key); 762 | } 763 | return names.sort(); 764 | }; 765 | 766 | // Extend a given object with all the properties in passed-in object(s). 767 | _.extend = function(obj) { 768 | each(slice.call(arguments, 1), function(source) { 769 | if (source) { 770 | for (var prop in source) { 771 | obj[prop] = source[prop]; 772 | } 773 | } 774 | }); 775 | return obj; 776 | }; 777 | 778 | // Return a copy of the object only containing the whitelisted properties. 779 | _.pick = function(obj) { 780 | var copy = {}; 781 | var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); 782 | each(keys, function(key) { 783 | if (key in obj) copy[key] = obj[key]; 784 | }); 785 | return copy; 786 | }; 787 | 788 | // Return a copy of the object without the blacklisted properties. 789 | _.omit = function(obj) { 790 | var copy = {}; 791 | var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); 792 | for (var key in obj) { 793 | if (!_.contains(keys, key)) copy[key] = obj[key]; 794 | } 795 | return copy; 796 | }; 797 | 798 | // Fill in a given object with default properties. 799 | _.defaults = function(obj) { 800 | each(slice.call(arguments, 1), function(source) { 801 | if (source) { 802 | for (var prop in source) { 803 | if (obj[prop] == null) obj[prop] = source[prop]; 804 | } 805 | } 806 | }); 807 | return obj; 808 | }; 809 | 810 | // Create a (shallow-cloned) duplicate of an object. 811 | _.clone = function(obj) { 812 | if (!_.isObject(obj)) return obj; 813 | return _.isArray(obj) ? obj.slice() : _.extend({}, obj); 814 | }; 815 | 816 | // Invokes interceptor with the obj, and then returns obj. 817 | // The primary purpose of this method is to "tap into" a method chain, in 818 | // order to perform operations on intermediate results within the chain. 819 | _.tap = function(obj, interceptor) { 820 | interceptor(obj); 821 | return obj; 822 | }; 823 | 824 | // Internal recursive comparison function for `isEqual`. 825 | var eq = function(a, b, aStack, bStack) { 826 | // Identical objects are equal. `0 === -0`, but they aren't identical. 827 | // See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal. 828 | if (a === b) return a !== 0 || 1 / a == 1 / b; 829 | // A strict comparison is necessary because `null == undefined`. 830 | if (a == null || b == null) return a === b; 831 | // Unwrap any wrapped objects. 832 | if (a instanceof _) a = a._wrapped; 833 | if (b instanceof _) b = b._wrapped; 834 | // Compare `[[Class]]` names. 835 | var className = toString.call(a); 836 | if (className != toString.call(b)) return false; 837 | switch (className) { 838 | // Strings, numbers, dates, and booleans are compared by value. 839 | case '[object String]': 840 | // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is 841 | // equivalent to `new String("5")`. 842 | return a == String(b); 843 | case '[object Number]': 844 | // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for 845 | // other numeric values. 846 | return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b); 847 | case '[object Date]': 848 | case '[object Boolean]': 849 | // Coerce dates and booleans to numeric primitive values. Dates are compared by their 850 | // millisecond representations. Note that invalid dates with millisecond representations 851 | // of `NaN` are not equivalent. 852 | return +a == +b; 853 | // RegExps are compared by their source patterns and flags. 854 | case '[object RegExp]': 855 | return a.source == b.source && 856 | a.global == b.global && 857 | a.multiline == b.multiline && 858 | a.ignoreCase == b.ignoreCase; 859 | } 860 | if (typeof a != 'object' || typeof b != 'object') return false; 861 | // Assume equality for cyclic structures. The algorithm for detecting cyclic 862 | // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. 863 | var length = aStack.length; 864 | while (length--) { 865 | // Linear search. Performance is inversely proportional to the number of 866 | // unique nested structures. 867 | if (aStack[length] == a) return bStack[length] == b; 868 | } 869 | // Add the first object to the stack of traversed objects. 870 | aStack.push(a); 871 | bStack.push(b); 872 | var size = 0, result = true; 873 | // Recursively compare objects and arrays. 874 | if (className == '[object Array]') { 875 | // Compare array lengths to determine if a deep comparison is necessary. 876 | size = a.length; 877 | result = size == b.length; 878 | if (result) { 879 | // Deep compare the contents, ignoring non-numeric properties. 880 | while (size--) { 881 | if (!(result = eq(a[size], b[size], aStack, bStack))) break; 882 | } 883 | } 884 | } else { 885 | // Objects with different constructors are not equivalent, but `Object`s 886 | // from different frames are. 887 | var aCtor = a.constructor, bCtor = b.constructor; 888 | if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) && 889 | _.isFunction(bCtor) && (bCtor instanceof bCtor))) { 890 | return false; 891 | } 892 | // Deep compare objects. 893 | for (var key in a) { 894 | if (_.has(a, key)) { 895 | // Count the expected number of properties. 896 | size++; 897 | // Deep compare each member. 898 | if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break; 899 | } 900 | } 901 | // Ensure that both objects contain the same number of properties. 902 | if (result) { 903 | for (key in b) { 904 | if (_.has(b, key) && !(size--)) break; 905 | } 906 | result = !size; 907 | } 908 | } 909 | // Remove the first object from the stack of traversed objects. 910 | aStack.pop(); 911 | bStack.pop(); 912 | return result; 913 | }; 914 | 915 | // Perform a deep comparison to check if two objects are equal. 916 | _.isEqual = function(a, b) { 917 | return eq(a, b, [], []); 918 | }; 919 | 920 | // Is a given array, string, or object empty? 921 | // An "empty" object has no enumerable own-properties. 922 | _.isEmpty = function(obj) { 923 | if (obj == null) return true; 924 | if (_.isArray(obj) || _.isString(obj)) return obj.length === 0; 925 | for (var key in obj) if (_.has(obj, key)) return false; 926 | return true; 927 | }; 928 | 929 | // Is a given value a DOM element? 930 | _.isElement = function(obj) { 931 | return !!(obj && obj.nodeType === 1); 932 | }; 933 | 934 | // Is a given value an array? 935 | // Delegates to ECMA5's native Array.isArray 936 | _.isArray = nativeIsArray || function(obj) { 937 | return toString.call(obj) == '[object Array]'; 938 | }; 939 | 940 | // Is a given variable an object? 941 | _.isObject = function(obj) { 942 | return obj === Object(obj); 943 | }; 944 | 945 | // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp. 946 | each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) { 947 | _['is' + name] = function(obj) { 948 | return toString.call(obj) == '[object ' + name + ']'; 949 | }; 950 | }); 951 | 952 | // Define a fallback version of the method in browsers (ahem, IE), where 953 | // there isn't any inspectable "Arguments" type. 954 | if (!_.isArguments(arguments)) { 955 | _.isArguments = function(obj) { 956 | return !!(obj && _.has(obj, 'callee')); 957 | }; 958 | } 959 | 960 | // Optimize `isFunction` if appropriate. 961 | if (typeof (/./) !== 'function') { 962 | _.isFunction = function(obj) { 963 | return typeof obj === 'function'; 964 | }; 965 | } 966 | 967 | // Is a given object a finite number? 968 | _.isFinite = function(obj) { 969 | return isFinite(obj) && !isNaN(parseFloat(obj)); 970 | }; 971 | 972 | // Is the given value `NaN`? (NaN is the only number which does not equal itself). 973 | _.isNaN = function(obj) { 974 | return _.isNumber(obj) && obj != +obj; 975 | }; 976 | 977 | // Is a given value a boolean? 978 | _.isBoolean = function(obj) { 979 | return obj === true || obj === false || toString.call(obj) == '[object Boolean]'; 980 | }; 981 | 982 | // Is a given value equal to null? 983 | _.isNull = function(obj) { 984 | return obj === null; 985 | }; 986 | 987 | // Is a given variable undefined? 988 | _.isUndefined = function(obj) { 989 | return obj === void 0; 990 | }; 991 | 992 | // Shortcut function for checking if an object has a given property directly 993 | // on itself (in other words, not on a prototype). 994 | _.has = function(obj, key) { 995 | return hasOwnProperty.call(obj, key); 996 | }; 997 | 998 | // Utility Functions 999 | // ----------------- 1000 | 1001 | // Run Underscore.js in *noConflict* mode, returning the `_` variable to its 1002 | // previous owner. Returns a reference to the Underscore object. 1003 | _.noConflict = function() { 1004 | root._ = previousUnderscore; 1005 | return this; 1006 | }; 1007 | 1008 | // Keep the identity function around for default iterators. 1009 | _.identity = function(value) { 1010 | return value; 1011 | }; 1012 | 1013 | // Run a function **n** times. 1014 | _.times = function(n, iterator, context) { 1015 | var accum = Array(n); 1016 | for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i); 1017 | return accum; 1018 | }; 1019 | 1020 | // Return a random integer between min and max (inclusive). 1021 | _.random = function(min, max) { 1022 | if (max == null) { 1023 | max = min; 1024 | min = 0; 1025 | } 1026 | return min + Math.floor(Math.random() * (max - min + 1)); 1027 | }; 1028 | 1029 | // List of HTML entities for escaping. 1030 | var entityMap = { 1031 | escape: { 1032 | '&': '&', 1033 | '<': '<', 1034 | '>': '>', 1035 | '"': '"', 1036 | "'": ''', 1037 | '/': '/' 1038 | } 1039 | }; 1040 | entityMap.unescape = _.invert(entityMap.escape); 1041 | 1042 | // Regexes containing the keys and values listed immediately above. 1043 | var entityRegexes = { 1044 | escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'), 1045 | unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g') 1046 | }; 1047 | 1048 | // Functions for escaping and unescaping strings to/from HTML interpolation. 1049 | _.each(['escape', 'unescape'], function(method) { 1050 | _[method] = function(string) { 1051 | if (string == null) return ''; 1052 | return ('' + string).replace(entityRegexes[method], function(match) { 1053 | return entityMap[method][match]; 1054 | }); 1055 | }; 1056 | }); 1057 | 1058 | // If the value of the named property is a function then invoke it; 1059 | // otherwise, return it. 1060 | _.result = function(object, property) { 1061 | if (object == null) return null; 1062 | var value = object[property]; 1063 | return _.isFunction(value) ? value.call(object) : value; 1064 | }; 1065 | 1066 | // Add your own custom functions to the Underscore object. 1067 | _.mixin = function(obj) { 1068 | each(_.functions(obj), function(name){ 1069 | var func = _[name] = obj[name]; 1070 | _.prototype[name] = function() { 1071 | var args = [this._wrapped]; 1072 | push.apply(args, arguments); 1073 | return result.call(this, func.apply(_, args)); 1074 | }; 1075 | }); 1076 | }; 1077 | 1078 | // Generate a unique integer id (unique within the entire client session). 1079 | // Useful for temporary DOM ids. 1080 | var idCounter = 0; 1081 | _.uniqueId = function(prefix) { 1082 | var id = ++idCounter + ''; 1083 | return prefix ? prefix + id : id; 1084 | }; 1085 | 1086 | // By default, Underscore uses ERB-style template delimiters, change the 1087 | // following template settings to use alternative delimiters. 1088 | _.templateSettings = { 1089 | evaluate : /<%([\s\S]+?)%>/g, 1090 | interpolate : /<%=([\s\S]+?)%>/g, 1091 | escape : /<%-([\s\S]+?)%>/g 1092 | }; 1093 | 1094 | // When customizing `templateSettings`, if you don't want to define an 1095 | // interpolation, evaluation or escaping regex, we need one that is 1096 | // guaranteed not to match. 1097 | var noMatch = /(.)^/; 1098 | 1099 | // Certain characters need to be escaped so that they can be put into a 1100 | // string literal. 1101 | var escapes = { 1102 | "'": "'", 1103 | '\\': '\\', 1104 | '\r': 'r', 1105 | '\n': 'n', 1106 | '\t': 't', 1107 | '\u2028': 'u2028', 1108 | '\u2029': 'u2029' 1109 | }; 1110 | 1111 | var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; 1112 | 1113 | // JavaScript micro-templating, similar to John Resig's implementation. 1114 | // Underscore templating handles arbitrary delimiters, preserves whitespace, 1115 | // and correctly escapes quotes within interpolated code. 1116 | _.template = function(text, data, settings) { 1117 | var render; 1118 | settings = _.defaults({}, settings, _.templateSettings); 1119 | 1120 | // Combine delimiters into one regular expression via alternation. 1121 | var matcher = new RegExp([ 1122 | (settings.escape || noMatch).source, 1123 | (settings.interpolate || noMatch).source, 1124 | (settings.evaluate || noMatch).source 1125 | ].join('|') + '|$', 'g'); 1126 | 1127 | // Compile the template source, escaping string literals appropriately. 1128 | var index = 0; 1129 | var source = "__p+='"; 1130 | text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { 1131 | source += text.slice(index, offset) 1132 | .replace(escaper, function(match) { return '\\' + escapes[match]; }); 1133 | 1134 | if (escape) { 1135 | source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; 1136 | } 1137 | if (interpolate) { 1138 | source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; 1139 | } 1140 | if (evaluate) { 1141 | source += "';\n" + evaluate + "\n__p+='"; 1142 | } 1143 | index = offset + match.length; 1144 | return match; 1145 | }); 1146 | source += "';\n"; 1147 | 1148 | // If a variable is not specified, place data values in local scope. 1149 | if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; 1150 | 1151 | source = "var __t,__p='',__j=Array.prototype.join," + 1152 | "print=function(){__p+=__j.call(arguments,'');};\n" + 1153 | source + "return __p;\n"; 1154 | 1155 | try { 1156 | render = new Function(settings.variable || 'obj', '_', source); 1157 | } catch (e) { 1158 | e.source = source; 1159 | throw e; 1160 | } 1161 | 1162 | if (data) return render(data, _); 1163 | var template = function(data) { 1164 | return render.call(this, data, _); 1165 | }; 1166 | 1167 | // Provide the compiled function source as a convenience for precompilation. 1168 | template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; 1169 | 1170 | return template; 1171 | }; 1172 | 1173 | // Add a "chain" function, which will delegate to the wrapper. 1174 | _.chain = function(obj) { 1175 | return _(obj).chain(); 1176 | }; 1177 | 1178 | // OOP 1179 | // --------------- 1180 | // If Underscore is called as a function, it returns a wrapped object that 1181 | // can be used OO-style. This wrapper holds altered versions of all the 1182 | // underscore functions. Wrapped objects may be chained. 1183 | 1184 | // Helper function to continue chaining intermediate results. 1185 | var result = function(obj) { 1186 | return this._chain ? _(obj).chain() : obj; 1187 | }; 1188 | 1189 | // Add all of the Underscore functions to the wrapper object. 1190 | _.mixin(_); 1191 | 1192 | // Add all mutator Array functions to the wrapper. 1193 | each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { 1194 | var method = ArrayProto[name]; 1195 | _.prototype[name] = function() { 1196 | var obj = this._wrapped; 1197 | method.apply(obj, arguments); 1198 | if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0]; 1199 | return result.call(this, obj); 1200 | }; 1201 | }); 1202 | 1203 | // Add all accessor Array functions to the wrapper. 1204 | each(['concat', 'join', 'slice'], function(name) { 1205 | var method = ArrayProto[name]; 1206 | _.prototype[name] = function() { 1207 | return result.call(this, method.apply(this._wrapped, arguments)); 1208 | }; 1209 | }); 1210 | 1211 | _.extend(_.prototype, { 1212 | 1213 | // Start chaining a wrapped Underscore object. 1214 | chain: function() { 1215 | this._chain = true; 1216 | return this; 1217 | }, 1218 | 1219 | // Extracts the result from a wrapped and chained object. 1220 | value: function() { 1221 | return this._wrapped; 1222 | } 1223 | 1224 | }); 1225 | 1226 | }).call(this); 1227 | --------------------------------------------------------------------------------