├── 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 |
4 |
--------------------------------------------------------------------------------
/assets/js/templates/app/temp.html:
--------------------------------------------------------------------------------
1 |
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 | <%= title %>
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 |
3 | checked="checked" <% } %>>
4 | <%= label %>
5 |
6 |
7 |
--------------------------------------------------------------------------------
/assets/js/templates/popover/popover-input.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/js/templates/popover/popover-textarea.html:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/assets/js/templates/snippet/button.html:
--------------------------------------------------------------------------------
1 |
2 |
8 |
--------------------------------------------------------------------------------
/assets/js/templates/snippet/filebutton.html:
--------------------------------------------------------------------------------
1 |
2 |
8 |
--------------------------------------------------------------------------------
/assets/js/templates/snippet/textarea.html:
--------------------------------------------------------------------------------
1 |
2 |
8 |
--------------------------------------------------------------------------------
/assets/js/templates/snippet/dateinput.html:
--------------------------------------------------------------------------------
1 |
2 |
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 |
--------------------------------------------------------------------------------
/assets/js/templates/popover/popover-main.html:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/assets/js/templates/popover/popover-select.html:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/assets/js/templates/snippet/buttondouble.html:
--------------------------------------------------------------------------------
1 |
2 |
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 |
11 |
--------------------------------------------------------------------------------
/assets/js/templates/snippet/selectbasic.html:
--------------------------------------------------------------------------------
1 |
2 |
10 |
--------------------------------------------------------------------------------
/assets/js/templates/snippet/selectmultiple.html:
--------------------------------------------------------------------------------
1 |
2 |
10 |
--------------------------------------------------------------------------------
/assets/js/templates/snippet/multipleradiosinline.html:
--------------------------------------------------------------------------------
1 |
2 |
11 |
--------------------------------------------------------------------------------
/assets/js/templates/snippet/multiplecheckboxes.html:
--------------------------------------------------------------------------------
1 |
2 |
13 |
--------------------------------------------------------------------------------
/assets/js/templates/snippet/multipleradios.html:
--------------------------------------------------------------------------------
1 |
2 |
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 |
9 |
--------------------------------------------------------------------------------
/assets/js/templates/snippet/searchinput.html:
--------------------------------------------------------------------------------
1 |
2 |
9 |
--------------------------------------------------------------------------------
/assets/js/templates/snippet/passwordinput.html:
--------------------------------------------------------------------------------
1 |
2 |
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 |
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 |
12 |
--------------------------------------------------------------------------------
/assets/js/templates/snippet/prependedtext.html:
--------------------------------------------------------------------------------
1 |
2 |
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 |
14 |
--------------------------------------------------------------------------------
/assets/js/templates/snippet/buttondropdown.html:
--------------------------------------------------------------------------------
1 |
2 |
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 | [](https://www.npmjs.com/package/bootstrap-form-builder-v4) [](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 | [](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 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
Your Form
55 |
56 |
57 |
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 |
--------------------------------------------------------------------------------